Completed
Push — develop ( 3560f1...5fce89 )
by Adrien
21:58 queued 15:06
created

Worksheet::getShowSummaryRight()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

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...
342
        // Set page setup
343 81
        $this->pageSetup = new Worksheet\PageSetup();
344
        // Set page margins
345 81
        $this->pageMargins = new Worksheet\PageMargins();
346
        // Set page header/footer
347 81
        $this->headerFooter = new Worksheet\HeaderFooter();
348
        // Set sheet view
349 81
        $this->sheetView = new Worksheet\SheetView();
350
        // Drawing collection
351 81
        $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...
352
        // Chart collection
353 81
        $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...
354
        // Protection
355 81
        $this->protection = new Worksheet\Protection();
356
        // Default row dimension
357 81
        $this->defaultRowDimension = new Worksheet\RowDimension(null);
358
        // Default column dimension
359 81
        $this->defaultColumnDimension = new Worksheet\ColumnDimension(null);
360 81
        $this->autoFilter = new Worksheet\AutoFilter(null, $this);
361 81
    }
362
363
    /**
364
     * Disconnect all cells from this Worksheet object,
365
     * typically so that the worksheet object can be unset.
366
     */
367 1
    public function disconnectCells()
368
    {
369 1
        if ($this->cellCollection !== null) {
370 1
            $this->cellCollection->unsetWorksheetCells();
371 1
            $this->cellCollection = null;
372
        }
373
        //    detach ourself from the workbook, so that it can then delete this worksheet successfully
374 1
        $this->parent = null;
375 1
    }
376
377
    /**
378
     * Code to execute when this worksheet is unset().
379
     */
380 1
    public function __destruct()
381
    {
382 1
        Calculation::getInstance($this->parent)->clearCalculationCacheForWorksheet($this->title);
383
384 1
        $this->disconnectCells();
385 1
    }
386
387
    /**
388
     * Return the cache controller for the cell collection.
389
     *
390
     * @return CachedObjectStorage_xxx
391
     */
392 73
    public function getCellCacheController()
393
    {
394 73
        return $this->cellCollection;
395
    }
396
397
    /**
398
     * Get array of invalid characters for sheet title.
399
     *
400
     * @return array
401
     */
402
    public static function getInvalidCharacters()
403
    {
404
        return self::$invalidCharacters;
405
    }
406
407
    /**
408
     * Check sheet code name for valid Excel syntax.
409
     *
410
     * @param string $pValue The string to check
411
     *
412
     * @throws Exception
413
     *
414
     * @return string The valid string
415
     */
416 81
    private static function checkSheetCodeName($pValue)
417
    {
418 81
        $CharCount = Shared\StringHelper::countCharacters($pValue);
419 81
        if ($CharCount == 0) {
420
            throw new Exception('Sheet code name cannot be empty.');
421
        }
422
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ] and  first and last characters cannot be a "'"
423 81
        if ((str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) ||
424 81
            (Shared\StringHelper::substring($pValue, -1, 1) == '\'') ||
425 81
            (Shared\StringHelper::substring($pValue, 0, 1) == '\'')) {
426
            throw new Exception('Invalid character found in sheet code name');
427
        }
428
429
        // Maximum 31 characters allowed for sheet title
430 81
        if ($CharCount > 31) {
431
            throw new Exception('Maximum 31 characters allowed in sheet code name.');
432
        }
433
434 81
        return $pValue;
435
    }
436
437
    /**
438
     * Check sheet title for valid Excel syntax.
439
     *
440
     * @param string $pValue The string to check
441
     *
442
     * @throws Exception
443
     *
444
     * @return string The valid string
445
     */
446 81
    private static function checkSheetTitle($pValue)
447
    {
448
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ]
449 81
        if (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) {
450
            throw new Exception('Invalid character found in sheet title');
451
        }
452
453
        // Maximum 31 characters allowed for sheet title
454 81
        if (Shared\StringHelper::countCharacters($pValue) > 31) {
455
            throw new Exception('Maximum 31 characters allowed in sheet title.');
456
        }
457
458 81
        return $pValue;
459
    }
460
461
    /**
462
     * Get collection of cells.
463
     *
464
     * @param bool $pSorted Also sort the cell collection?
465
     *
466
     * @return Cell[]
467
     */
468 64
    public function getCellCollection($pSorted = true)
469
    {
470 64
        if ($pSorted) {
471
            // Re-order cell collection
472 63
            return $this->sortCellCollection();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->sortCellCollection(); (PhpOffice\PhpSpreadsheet\Worksheet) is incompatible with the return type documented by PhpOffice\PhpSpreadsheet...heet::getCellCollection of type PhpOffice\PhpSpreadsheet\Cell[].

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
473
        }
474 63
        if ($this->cellCollection !== null) {
475 63
            return $this->cellCollection->getCellList();
476
        }
477
478
        return [];
479
    }
480
481
    /**
482
     * Sort collection of cells.
483
     *
484
     * @return Worksheet
485
     */
486 63
    public function sortCellCollection()
487
    {
488 63
        if ($this->cellCollection !== null) {
489 63
            return $this->cellCollection->getSortedCellList();
490
        }
491
492
        return [];
0 ignored issues
show
Bug Best Practice introduced by
The return type of return array(); (array) is incompatible with the return type documented by PhpOffice\PhpSpreadsheet...eet::sortCellCollection of type PhpOffice\PhpSpreadsheet\Worksheet.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
493
    }
494
495
    /**
496
     * Get collection of row dimensions.
497
     *
498
     * @return Worksheet\RowDimension[]
499
     */
500 62
    public function getRowDimensions()
501
    {
502 62
        return $this->rowDimensions;
503
    }
504
505
    /**
506
     * Get default row dimension.
507
     *
508
     * @return Worksheet\RowDimension
509
     */
510 62
    public function getDefaultRowDimension()
511
    {
512 62
        return $this->defaultRowDimension;
513
    }
514
515
    /**
516
     * Get collection of column dimensions.
517
     *
518
     * @return Worksheet\ColumnDimension[]
519
     */
520 62
    public function getColumnDimensions()
521
    {
522 62
        return $this->columnDimensions;
523
    }
524
525
    /**
526
     * Get default column dimension.
527
     *
528
     * @return Worksheet\ColumnDimension
529
     */
530 58
    public function getDefaultColumnDimension()
531
    {
532 58
        return $this->defaultColumnDimension;
533
    }
534
535
    /**
536
     * Get collection of drawings.
537
     *
538
     * @return Worksheet\BaseDrawing[]
539
     */
540 62
    public function getDrawingCollection()
541
    {
542 62
        return $this->drawingCollection;
543
    }
544
545
    /**
546
     * Get collection of charts.
547
     *
548
     * @return Chart[]
549
     */
550 14
    public function getChartCollection()
551
    {
552 14
        return $this->chartCollection;
553
    }
554
555
    /**
556
     * Add chart.
557
     *
558
     * @param Chart $pChart
559
     * @param int|null $iChartIndex Index where chart should go (0,1,..., or null for last)
560
     *
561
     * @return Chart
562
     */
563 14
    public function addChart(Chart $pChart = null, $iChartIndex = null)
564
    {
565 14
        $pChart->setWorksheet($this);
0 ignored issues
show
Bug introduced by
It seems like $pChart is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
566 14
        if (is_null($iChartIndex)) {
567 14
            $this->chartCollection[] = $pChart;
568
        } else {
569
            // Insert the chart at the requested index
570
            array_splice($this->chartCollection, $iChartIndex, 0, [$pChart]);
571
        }
572
573 14
        return $pChart;
574
    }
575
576
    /**
577
     * Return the count of charts on this worksheet.
578
     *
579
     * @return int The number of charts
580
     */
581 14
    public function getChartCount()
582
    {
583 14
        return count($this->chartCollection);
584
    }
585
586
    /**
587
     * Get a chart by its index position.
588
     *
589
     * @param string $index Chart index position
590
     *
591
     * @throws Exception
592
     *
593
     * @return false|Chart
594
     */
595 13
    public function getChartByIndex($index = null)
596
    {
597 13
        $chartCount = count($this->chartCollection);
598 13
        if ($chartCount == 0) {
599
            return false;
600
        }
601 13
        if (is_null($index)) {
602
            $index = --$chartCount;
603
        }
604 13
        if (!isset($this->chartCollection[$index])) {
605
            return false;
606
        }
607
608 13
        return $this->chartCollection[$index];
609
    }
610
611
    /**
612
     * Return an array of the names of charts on this worksheet.
613
     *
614
     * @throws Exception
615
     *
616
     * @return string[] The names of charts
617
     */
618 1
    public function getChartNames()
619
    {
620 1
        $chartNames = [];
621 1
        foreach ($this->chartCollection as $chart) {
622 1
            $chartNames[] = $chart->getName();
623
        }
624
625 1
        return $chartNames;
626
    }
627
628
    /**
629
     * Get a chart by name.
630
     *
631
     * @param string $chartName Chart name
632
     *
633
     * @throws Exception
634
     *
635
     * @return false|Chart
636
     */
637 1
    public function getChartByName($chartName = '')
638
    {
639 1
        $chartCount = count($this->chartCollection);
640 1
        if ($chartCount == 0) {
641
            return false;
642
        }
643 1
        foreach ($this->chartCollection as $index => $chart) {
644 1
            if ($chart->getName() == $chartName) {
645 1
                return $this->chartCollection[$index];
646
            }
647
        }
648
649
        return false;
650
    }
651
652
    /**
653
     * Refresh column dimensions.
654
     *
655
     * @return Worksheet
656
     */
657 15
    public function refreshColumnDimensions()
658
    {
659 15
        $currentColumnDimensions = $this->getColumnDimensions();
660 15
        $newColumnDimensions = [];
661
662 15
        foreach ($currentColumnDimensions as $objColumnDimension) {
663 15
            $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
664
        }
665
666 15
        $this->columnDimensions = $newColumnDimensions;
667
668 15
        return $this;
669
    }
670
671
    /**
672
     * Refresh row dimensions.
673
     *
674
     * @return Worksheet
675
     */
676 2
    public function refreshRowDimensions()
677
    {
678 2
        $currentRowDimensions = $this->getRowDimensions();
679 2
        $newRowDimensions = [];
680
681 2
        foreach ($currentRowDimensions as $objRowDimension) {
682 2
            $newRowDimensions[$objRowDimension->getRowIndex()] = $objRowDimension;
683
        }
684
685 2
        $this->rowDimensions = $newRowDimensions;
686
687 2
        return $this;
688
    }
689
690
    /**
691
     * Calculate worksheet dimension.
692
     *
693
     * @return string String containing the dimension of this worksheet
694
     */
695 61
    public function calculateWorksheetDimension()
696
    {
697
        // Return
698 61
        return 'A1' . ':' . $this->getHighestColumn() . $this->getHighestRow();
699
    }
700
701
    /**
702
     * Calculate worksheet data dimension.
703
     *
704
     * @return string String containing the dimension of this worksheet that actually contain data
705
     */
706
    public function calculateWorksheetDataDimension()
707
    {
708
        // Return
709
        return 'A1' . ':' . $this->getHighestDataColumn() . $this->getHighestDataRow();
710
    }
711
712
    /**
713
     * Calculate widths for auto-size columns.
714
     *
715
     * @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...
716
     */
717 46
    public function calculateColumnWidths()
718
    {
719
        // initialize $autoSizes array
720 46
        $autoSizes = [];
721 46
        foreach ($this->getColumnDimensions() as $colDimension) {
722 26
            if ($colDimension->getAutoSize()) {
723 26
                $autoSizes[$colDimension->getColumnIndex()] = -1;
724
            }
725
        }
726
727
        // There is only something to do if there are some auto-size columns
728 46
        if (!empty($autoSizes)) {
729
            // build list of cells references that participate in a merge
730 14
            $isMergeCell = [];
731 14
            foreach ($this->getMergeCells() as $cells) {
732 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...
733 11
                    $isMergeCell[$cellReference] = true;
734
                }
735
            }
736
737
            // loop through all cells in the worksheet
738 14
            foreach ($this->getCellCollection(false) as $cellID) {
739 14
                $cell = $this->getCell($cellID, false);
740 14
                if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) {
741
                    //Determine if cell is in merge range
742 14
                    $isMerged = isset($isMergeCell[$this->cellCollection->getCurrentAddress()]);
743
744
                    //By default merged cells should be ignored
745 14
                    $isMergedButProceed = false;
746
747
                    //The only exception is if it's a merge range value cell of a 'vertical' randge (1 column wide)
748 14
                    if ($isMerged && $cell->isMergeRangeValueCell()) {
749
                        $range = $cell->getMergeRange();
750
                        $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...
751
                        if ($rangeBoundaries[0] == 1) {
752
                            $isMergedButProceed = true;
753
                        }
754
                    }
755
756
                    // Determine width if cell does not participate in a merge or does and is a value cell of 1-column wide range
757 14
                    if (!$isMerged || $isMergedButProceed) {
758
                        // Calculated value
759
                        // To formatted string
760 14
                        $cellValue = Style\NumberFormat::toFormattedString(
761 14
                            $cell->getCalculatedValue(),
762 14
                            $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()
763
                        );
764
765 14
                        $autoSizes[$this->cellCollection->getCurrentColumn()] = max(
766 14
                            (float) $autoSizes[$this->cellCollection->getCurrentColumn()],
767 14
                            (float) Shared\Font::calculateColumnWidth(
768 14
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
769
                                $cellValue,
770 14
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(),
771 14
                                $this->getParent()->getDefaultStyle()->getFont()
772
                            )
773
                        );
774
                    }
775
                }
776
            }
777
778
            // adjust column widths
779 14
            foreach ($autoSizes as $columnIndex => $width) {
780 14
                if ($width == -1) {
781
                    $width = $this->getDefaultColumnDimension()->getWidth();
782
                }
783 14
                $this->getColumnDimension($columnIndex)->setWidth($width);
784
            }
785
        }
786
787 46
        return $this;
788
    }
789
790
    /**
791
     * Get parent.
792
     *
793
     * @return Spreadsheet
794
     */
795 81
    public function getParent()
796
    {
797 81
        return $this->parent;
798
    }
799
800
    /**
801
     * Re-bind parent.
802
     *
803
     * @param Spreadsheet $parent
804
     *
805
     * @return Worksheet
806
     */
807 1
    public function rebindParent(Spreadsheet $parent)
808
    {
809 1
        if ($this->parent !== null) {
810 1
            $namedRanges = $this->parent->getNamedRanges();
811 1
            foreach ($namedRanges as $namedRange) {
812
                $parent->addNamedRange($namedRange);
813
            }
814
815 1
            $this->parent->removeSheetByIndex(
816 1
                $this->parent->getIndex($this)
817
            );
818
        }
819 1
        $this->parent = $parent;
820
821 1
        return $this;
822
    }
823
824
    /**
825
     * Get title.
826
     *
827
     * @return string
828
     */
829 81
    public function getTitle()
830
    {
831 81
        return $this->title;
832
    }
833
834
    /**
835
     * Set title.
836
     *
837
     * @param string $pValue String containing the dimension of this worksheet
838
     * @param string $updateFormulaCellReferences boolean Flag indicating whether cell references in formulae should
839
     *            be updated to reflect the new sheet name.
840
     *          This should be left as the default true, unless you are
841
     *          certain that no formula cells on any worksheet contain
842
     *          references to this worksheet
843
     *
844
     * @return Worksheet
845
     */
846 81
    public function setTitle($pValue = 'Worksheet', $updateFormulaCellReferences = true)
847
    {
848
        // Is this a 'rename' or not?
849 81
        if ($this->getTitle() == $pValue) {
850 4
            return $this;
851
        }
852
853
        // Syntax check
854 81
        self::checkSheetTitle($pValue);
855
856
        // Old title
857 81
        $oldTitle = $this->getTitle();
858
859 81
        if ($this->parent) {
860
            // Is there already such sheet name?
861 73
            if ($this->parent->sheetNameExists($pValue)) {
862
                // Use name, but append with lowest possible integer
863
864 2
                if (Shared\StringHelper::countCharacters($pValue) > 29) {
865
                    $pValue = Shared\StringHelper::substring($pValue, 0, 29);
866
                }
867 2
                $i = 1;
868 2 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...
869
                    ++$i;
870
                    if ($i == 10) {
871
                        if (Shared\StringHelper::countCharacters($pValue) > 28) {
872
                            $pValue = Shared\StringHelper::substring($pValue, 0, 28);
873
                        }
874
                    } elseif ($i == 100) {
875
                        if (Shared\StringHelper::countCharacters($pValue) > 27) {
876
                            $pValue = Shared\StringHelper::substring($pValue, 0, 27);
877
                        }
878
                    }
879
                }
880
881 2
                $altTitle = $pValue . ' ' . $i;
882
883 2
                return $this->setTitle($altTitle, $updateFormulaCellReferences);
884
            }
885
        }
886
887
        // Set title
888 81
        $this->title = $pValue;
889 81
        $this->dirty = true;
890
891 81
        if ($this->parent && $this->parent->getCalculationEngine()) {
892
            // New title
893 73
            $newTitle = $this->getTitle();
894 73
            $this->parent->getCalculationEngine()
895 73
                ->renameCalculationCacheForWorksheet($oldTitle, $newTitle);
896 73
            if ($updateFormulaCellReferences) {
897 27
                ReferenceHelper::getInstance()->updateNamedFormulas($this->parent, $oldTitle, $newTitle);
898
            }
899
        }
900
901 81
        return $this;
902
    }
903
904
    /**
905
     * Get sheet state.
906
     *
907
     * @return string Sheet state (visible, hidden, veryHidden)
908
     */
909 58
    public function getSheetState()
910
    {
911 58
        return $this->sheetState;
912
    }
913
914
    /**
915
     * Set sheet state.
916
     *
917
     * @param string $value Sheet state (visible, hidden, veryHidden)
918
     *
919
     * @return Worksheet
920
     */
921 81
    public function setSheetState($value = self::SHEETSTATE_VISIBLE)
922
    {
923 81
        $this->sheetState = $value;
924
925 81
        return $this;
926
    }
927
928
    /**
929
     * Get page setup.
930
     *
931
     * @return Worksheet\PageSetup
932
     */
933 62
    public function getPageSetup()
934
    {
935 62
        return $this->pageSetup;
936
    }
937
938
    /**
939
     * Set page setup.
940
     *
941
     * @param Worksheet\PageSetup $pValue
942
     *
943
     * @return Worksheet
944
     */
945
    public function setPageSetup(Worksheet\PageSetup $pValue)
946
    {
947
        $this->pageSetup = $pValue;
948
949
        return $this;
950
    }
951
952
    /**
953
     * Get page margins.
954
     *
955
     * @return Worksheet\PageMargins
956
     */
957 62
    public function getPageMargins()
958
    {
959 62
        return $this->pageMargins;
960
    }
961
962
    /**
963
     * Set page margins.
964
     *
965
     * @param Worksheet\PageMargins $pValue
966
     *
967
     * @return Worksheet
968
     */
969
    public function setPageMargins(Worksheet\PageMargins $pValue)
970
    {
971
        $this->pageMargins = $pValue;
972
973
        return $this;
974
    }
975
976
    /**
977
     * Get page header/footer.
978
     *
979
     * @return Worksheet\HeaderFooter
980
     */
981 62
    public function getHeaderFooter()
982
    {
983 62
        return $this->headerFooter;
984
    }
985
986
    /**
987
     * Set page header/footer.
988
     *
989
     * @param Worksheet\HeaderFooter $pValue
990
     *
991
     * @return Worksheet
992
     */
993
    public function setHeaderFooter(Worksheet\HeaderFooter $pValue)
994
    {
995
        $this->headerFooter = $pValue;
996
997
        return $this;
998
    }
999
1000
    /**
1001
     * Get sheet view.
1002
     *
1003
     * @return Worksheet\SheetView
1004
     */
1005 58
    public function getSheetView()
1006
    {
1007 58
        return $this->sheetView;
1008
    }
1009
1010
    /**
1011
     * Set sheet view.
1012
     *
1013
     * @param Worksheet\SheetView $pValue
1014
     *
1015
     * @return Worksheet
1016
     */
1017
    public function setSheetView(Worksheet\SheetView $pValue)
1018
    {
1019
        $this->sheetView = $pValue;
1020
1021
        return $this;
1022
    }
1023
1024
    /**
1025
     * Get Protection.
1026
     *
1027
     * @return Worksheet\Protection
1028
     */
1029 62
    public function getProtection()
1030
    {
1031 62
        return $this->protection;
1032
    }
1033
1034
    /**
1035
     * Set Protection.
1036
     *
1037
     * @param Worksheet\Protection $pValue
1038
     *
1039
     * @return Worksheet
1040
     */
1041
    public function setProtection(Worksheet\Protection $pValue)
1042
    {
1043
        $this->protection = $pValue;
1044
        $this->dirty = true;
1045
1046
        return $this;
1047
    }
1048
1049
    /**
1050
     * Get highest worksheet column.
1051
     *
1052
     * @param string $row Return the data highest column for the specified row,
1053
     *                                     or the highest column of any row if no row number is passed
1054
     *
1055
     * @return string Highest column name
1056
     */
1057 66
    public function getHighestColumn($row = null)
1058
    {
1059 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...
1060 66
            return $this->cachedHighestColumn;
1061
        }
1062
1063
        return $this->getHighestDataColumn($row);
1064
    }
1065
1066
    /**
1067
     * Get highest worksheet column that contains data.
1068
     *
1069
     * @param string $row Return the highest data column for the specified row,
1070
     *                                     or the highest data column of any row if no row number is passed
1071
     *
1072
     * @return string Highest column name that contains data
1073
     */
1074 13
    public function getHighestDataColumn($row = null)
1075
    {
1076 13
        return $this->cellCollection->getHighestColumn($row);
1077
    }
1078
1079
    /**
1080
     * Get highest worksheet row.
1081
     *
1082
     * @param string $column Return the highest data row for the specified column,
1083
     *                                     or the highest row of any column if no column letter is passed
1084
     *
1085
     * @return int Highest row number
1086
     */
1087 66
    public function getHighestRow($column = null)
1088
    {
1089 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...
1090 66
            return $this->cachedHighestRow;
1091
        }
1092
1093
        return $this->getHighestDataRow($column);
1094
    }
1095
1096
    /**
1097
     * Get highest worksheet row that contains data.
1098
     *
1099
     * @param string $column Return the highest data row for the specified column,
1100
     *                                     or the highest data row of any column if no column letter is passed
1101
     *
1102
     * @return string Highest row number that contains data
1103
     */
1104 15
    public function getHighestDataRow($column = null)
1105
    {
1106 15
        return $this->cellCollection->getHighestRow($column);
1107
    }
1108
1109
    /**
1110
     * Get highest worksheet column and highest row that have cell records.
1111
     *
1112
     * @return array Highest column name and highest row number
1113
     */
1114
    public function getHighestRowAndColumn()
1115
    {
1116
        return $this->cellCollection->getHighestRowAndColumn();
1117
    }
1118
1119
    /**
1120
     * Set a cell value.
1121
     *
1122
     * @param string $pCoordinate Coordinate of the cell
1123
     * @param mixed $pValue Value of the cell
1124
     * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
1125
     *
1126
     * @return Worksheet|Cell Depending on the last parameter being specified
1127
     */
1128 40
    public function setCellValue($pCoordinate = 'A1', $pValue = null, $returnCell = false)
1129
    {
1130 40
        $cell = $this->getCell(strtoupper($pCoordinate))->setValue($pValue);
1131
1132 40
        return ($returnCell) ? $cell : $this;
1133
    }
1134
1135
    /**
1136
     * Set a cell value by using numeric cell coordinates.
1137
     *
1138
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1139
     * @param int $pRow Numeric row coordinate of the cell
1140
     * @param mixed $pValue Value of the cell
1141
     * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
1142
     *
1143
     * @return Worksheet|Cell Depending on the last parameter being specified
1144
     */
1145
    public function setCellValueByColumnAndRow($pColumn = 0, $pRow = 1, $pValue = null, $returnCell = false)
1146
    {
1147
        $cell = $this->getCellByColumnAndRow($pColumn, $pRow)->setValue($pValue);
1148
1149
        return ($returnCell) ? $cell : $this;
1150
    }
1151
1152
    /**
1153
     * Set a cell value.
1154
     *
1155
     * @param string $pCoordinate Coordinate of the cell
1156
     * @param mixed $pValue Value of the cell
1157
     * @param string $pDataType Explicit data type
1158
     * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
1159
     *
1160
     * @return Worksheet|Cell Depending on the last parameter being specified
1161
     */
1162 1
    public function setCellValueExplicit($pCoordinate = 'A1', $pValue = null, $pDataType = Cell\DataType::TYPE_STRING, $returnCell = false)
1163
    {
1164
        // Set value
1165 1
        $cell = $this->getCell(strtoupper($pCoordinate))->setValueExplicit($pValue, $pDataType);
1166
1167 1
        return ($returnCell) ? $cell : $this;
1168
    }
1169
1170
    /**
1171
     * Set a cell value by using numeric cell coordinates.
1172
     *
1173
     * @param int $pColumn Numeric column coordinate of the cell
1174
     * @param int $pRow Numeric row coordinate of the cell
1175
     * @param mixed $pValue Value of the cell
1176
     * @param string $pDataType Explicit data type
1177
     * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
1178
     *
1179
     * @return Worksheet|Cell Depending on the last parameter being specified
1180
     */
1181
    public function setCellValueExplicitByColumnAndRow($pColumn = 0, $pRow = 1, $pValue = null, $pDataType = Cell\DataType::TYPE_STRING, $returnCell = false)
1182
    {
1183
        $cell = $this->getCellByColumnAndRow($pColumn, $pRow)->setValueExplicit($pValue, $pDataType);
1184
1185
        return ($returnCell) ? $cell : $this;
1186
    }
1187
1188
    /**
1189
     * Get cell at a specific coordinate.
1190
     *
1191
     * @param string $pCoordinate Coordinate of the cell
1192
     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
1193
     *                                       already exist, or a null should be returned instead
1194
     *
1195
     * @throws Exception
1196
     *
1197
     * @return null|Cell Cell that was found/created or null
1198
     */
1199 72
    public function getCell($pCoordinate = 'A1', $createIfNotExists = true)
1200
    {
1201
        // Check cell collection
1202 72
        if ($this->cellCollection->isDataSet(strtoupper($pCoordinate))) {
1203 70
            return $this->cellCollection->getCacheData($pCoordinate);
1204
        }
1205
1206
        // Worksheet reference?
1207 71 View Code Duplication
        if (strpos($pCoordinate, '!') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1208 1
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1209
1210 1
            return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
1211
        }
1212
1213
        // Named range?
1214 71
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1215 71
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1216
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1217
            if ($namedRange !== null) {
1218
                $pCoordinate = $namedRange->getRange();
1219
1220
                return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
1221
            }
1222
        }
1223
1224
        // Uppercase coordinate
1225 71
        $pCoordinate = strtoupper($pCoordinate);
1226
1227 71 View Code Duplication
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1228
            throw new Exception('Cell coordinate can not be a range of cells.');
1229 71
        } elseif (strpos($pCoordinate, '$') !== false) {
1230
            throw new Exception('Cell coordinate must not be absolute.');
1231
        }
1232
1233
        // Create new cell object, if required
1234 71
        return $createIfNotExists ? $this->createNewCell($pCoordinate) : null;
1235
    }
1236
1237
    /**
1238
     * Get cell at a specific coordinate by using numeric cell coordinates.
1239
     *
1240
     * @param string $pColumn Numeric column coordinate of the cell
1241
     * @param string $pRow Numeric row coordinate of the cell
1242
     * @param bool $createIfNotExists Flag indicating whether a new cell should be created if it doesn't
1243
     *                                       already exist, or a null should be returned instead
1244
     *
1245
     * @return null|Cell Cell that was found/created or null
1246
     */
1247 40
    public function getCellByColumnAndRow($pColumn = 0, $pRow = 1, $createIfNotExists = true)
1248
    {
1249 40
        $columnLetter = Cell::stringFromColumnIndex($pColumn);
1250 40
        $coordinate = $columnLetter . $pRow;
1251
1252 40
        if ($this->cellCollection->isDataSet($coordinate)) {
1253 39
            return $this->cellCollection->getCacheData($coordinate);
1254
        }
1255
1256
        // Create new cell object, if required
1257 22
        return $createIfNotExists ? $this->createNewCell($coordinate) : null;
1258
    }
1259
1260
    /**
1261
     * Create a new cell at the specified coordinate.
1262
     *
1263
     * @param string $pCoordinate Coordinate of the cell
1264
     *
1265
     * @return Cell Cell that was created
1266
     */
1267 73
    private function createNewCell($pCoordinate)
1268
    {
1269 73
        $cell = $this->cellCollection->addCacheData(
1270
            $pCoordinate,
1271 73
            new Cell(null, Cell\DataType::TYPE_NULL, $this)
1272
        );
1273 73
        $this->cellCollectionIsSorted = false;
1274
1275
        // Coordinates
1276 73
        $aCoordinates = Cell::coordinateFromString($pCoordinate);
1277 73
        if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($aCoordinates[0])) {
1278 64
            $this->cachedHighestColumn = $aCoordinates[0];
1279
        }
1280 73
        $this->cachedHighestRow = max($this->cachedHighestRow, $aCoordinates[1]);
0 ignored issues
show
Documentation Bug introduced by
It seems like max($this->cachedHighestRow, $aCoordinates[1]) can also be of type string. However, the property $cachedHighestRow is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1281
1282
        // Cell needs appropriate xfIndex from dimensions records
1283
        //    but don't create dimension records if they don't already exist
1284 73
        $rowDimension = $this->getRowDimension($aCoordinates[1], false);
1285 73
        $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
1286
1287 73
        if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
1288
            // then there is a row dimension with explicit style, assign it to the cell
1289
            $cell->setXfIndex($rowDimension->getXfIndex());
1290 73
        } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
1291
            // then there is a column dimension, assign it to the cell
1292
            $cell->setXfIndex($columnDimension->getXfIndex());
1293
        }
1294
1295 73
        return $cell;
1296
    }
1297
1298
    /**
1299
     * Does the cell at a specific coordinate exist?
1300
     *
1301
     * @param string $pCoordinate Coordinate of the cell
1302
     *
1303
     * @throws Exception
1304
     *
1305
     * @return bool
1306
     */
1307 42
    public function cellExists($pCoordinate = 'A1')
1308
    {
1309
        // Worksheet reference?
1310 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...
1311
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1312
1313
            return $this->parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
1314
        }
1315
1316
        // Named range?
1317 42
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1318 42
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1319
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1320
            if ($namedRange !== null) {
1321
                $pCoordinate = $namedRange->getRange();
1322
                if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
1323
                    if (!$namedRange->getLocalOnly()) {
1324
                        return $namedRange->getWorksheet()->cellExists($pCoordinate);
1325
                    }
1326
                    throw new Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
1327
                }
1328
            } else {
1329
                return false;
1330
            }
1331
        }
1332
1333
        // Uppercase coordinate
1334 42
        $pCoordinate = strtoupper($pCoordinate);
1335
1336 42
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
1337
            throw new Exception('Cell coordinate can not be a range of cells.');
1338 42
        } elseif (strpos($pCoordinate, '$') !== false) {
1339
            throw new Exception('Cell coordinate must not be absolute.');
1340
        }
1341
            // Coordinates
1342 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...
1343
1344
            // Cell exists?
1345 42
            return $this->cellCollection->isDataSet($pCoordinate);
1346
    }
1347
1348
    /**
1349
     * Cell at a specific coordinate by using numeric cell coordinates exists?
1350
     *
1351
     * @param string $pColumn Numeric column coordinate of the cell
1352
     * @param string $pRow Numeric row coordinate of the cell
1353
     *
1354
     * @return bool
1355
     */
1356 6
    public function cellExistsByColumnAndRow($pColumn = 0, $pRow = 1)
1357
    {
1358 6
        return $this->cellExists(Cell::stringFromColumnIndex($pColumn) . $pRow);
1359
    }
1360
1361
    /**
1362
     * Get row dimension at a specific row.
1363
     *
1364
     * @param int $pRow Numeric index of the row
1365
     * @param mixed $create
1366
     *
1367
     * @return Worksheet\RowDimension
1368
     */
1369 73
    public function getRowDimension($pRow = 1, $create = true)
1370
    {
1371
        // Found
1372 73
        $found = null;
0 ignored issues
show
Unused Code introduced by
$found is not used, you could remove the assignment.

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

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

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

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

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

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
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...
1568
1569
        // Add the style to the workbook if necessary
1570 2
        $workbook = $this->parent;
1571 2
        if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
1572
            // there is already such cell Xf in our collection
1573 1
            $xfIndex = $existingStyle->getIndex();
1574
        } else {
1575
            // we don't have such a cell Xf, need to add
1576 2
            $workbook->addCellXf($pCellStyle);
0 ignored issues
show
Bug introduced by
It seems like $pCellStyle defined by parameter $pCellStyle on line 1564 can be null; however, PhpOffice\PhpSpreadsheet\Spreadsheet::addCellXf() does not accept null, maybe add an additional type check?

It seems like you allow that null is being passed for a parameter, however the function which is called does not seem to accept null.

We recommend to add an additional type check (or disallow null for the parameter):

function notNullable(stdClass $x) { }

// Unsafe
function withoutCheck(stdClass $x = null) {
    notNullable($x);
}

// Safe - Alternative 1: Adding Additional Type-Check
function withCheck(stdClass $x = null) {
    if ($x instanceof stdClass) {
        notNullable($x);
    }
}

// Safe - Alternative 2: Changing Parameter
function withNonNullableParam(stdClass $x) {
    notNullable($x);
}
Loading history...
1577 2
            $xfIndex = $pCellStyle->getIndex();
1578
        }
1579
1580
        // Calculate range outer borders
1581 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1582
1583
        // Make sure we can loop upwards on rows and columns
1584 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...
1585
            $tmp = $rangeStart;
1586
            $rangeStart = $rangeEnd;
1587
            $rangeEnd = $tmp;
1588
        }
1589
1590
        // Loop through cells and apply styles
1591 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...
1592 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1593 2
                $this->getCell(Cell::stringFromColumnIndex($col - 1) . $row)->setXfIndex($xfIndex);
1594
            }
1595
        }
1596
1597 2
        return $this;
1598
    }
1599
1600
    /**
1601
     * Duplicate conditional style to a range of cells.
1602
     *
1603
     * Please note that this will overwrite existing cell styles for cells in range!
1604
     *
1605
     * @param Style\Conditional[] $pCellStyle Cell style to duplicate
1606
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1607
     *
1608
     * @throws Exception
1609
     *
1610
     * @return Worksheet
1611
     */
1612 2
    public function duplicateConditionalStyle(array $pCellStyle = null, $pRange = '')
1613
    {
1614 2
        foreach ($pCellStyle as $cellStyle) {
0 ignored issues
show
Bug introduced by
The expression $pCellStyle of type null|array<integer,objec...eet\Style\Conditional>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
1615 2
            if (!($cellStyle instanceof Style\Conditional)) {
1616 2
                throw new Exception('Style is not a conditional style');
1617
            }
1618
        }
1619
1620
        // Calculate range outer borders
1621 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1622
1623
        // Make sure we can loop upwards on rows and columns
1624 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...
1625
            $tmp = $rangeStart;
1626
            $rangeStart = $rangeEnd;
1627
            $rangeEnd = $tmp;
1628
        }
1629
1630
        // Loop through cells and apply styles
1631 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...
1632 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1633 2
                $this->setConditionalStyles(Cell::stringFromColumnIndex($col - 1) . $row, $pCellStyle);
1634
            }
1635
        }
1636
1637 2
        return $this;
1638
    }
1639
1640
    /**
1641
     * Set break on a cell.
1642
     *
1643
     * @param string $pCell Cell coordinate (e.g. A1)
1644
     * @param int $pBreak Break type (type of Worksheet::BREAK_*)
1645
     *
1646
     * @throws Exception
1647
     *
1648
     * @return Worksheet
1649
     */
1650 1
    public function setBreak($pCell = 'A1', $pBreak = self::BREAK_NONE)
1651
    {
1652
        // Uppercase coordinate
1653 1
        $pCell = strtoupper($pCell);
1654
1655 1
        if ($pCell != '') {
1656 1
            if ($pBreak == self::BREAK_NONE) {
1657
                if (isset($this->breaks[$pCell])) {
1658
                    unset($this->breaks[$pCell]);
1659
                }
1660
            } else {
1661 1
                $this->breaks[$pCell] = $pBreak;
1662
            }
1663
        } else {
1664
            throw new Exception('No cell coordinate specified.');
1665
        }
1666
1667 1
        return $this;
1668
    }
1669
1670
    /**
1671
     * Set break on a cell by using numeric cell coordinates.
1672
     *
1673
     * @param int $pColumn Numeric column coordinate of the cell
1674
     * @param int $pRow Numeric row coordinate of the cell
1675
     * @param int $pBreak Break type (type of \PhpOffice\PhpSpreadsheet\Worksheet::BREAK_*)
1676
     *
1677
     * @return Worksheet
1678
     */
1679
    public function setBreakByColumnAndRow($pColumn = 0, $pRow = 1, $pBreak = self::BREAK_NONE)
1680
    {
1681
        return $this->setBreak(Cell::stringFromColumnIndex($pColumn) . $pRow, $pBreak);
1682
    }
1683
1684
    /**
1685
     * Get breaks.
1686
     *
1687
     * @return array[]
1688
     */
1689 62
    public function getBreaks()
1690
    {
1691 62
        return $this->breaks;
1692
    }
1693
1694
    /**
1695
     * Set merge on a cell range.
1696
     *
1697
     * @param string $pRange Cell range (e.g. A1:E1)
1698
     *
1699
     * @throws Exception
1700
     *
1701
     * @return Worksheet
1702
     */
1703 17
    public function mergeCells($pRange = 'A1:A1')
1704
    {
1705
        // Uppercase coordinate
1706 17
        $pRange = strtoupper($pRange);
1707
1708 17
        if (strpos($pRange, ':') !== false) {
1709 17
            $this->mergeCells[$pRange] = $pRange;
1710
1711
            // make sure cells are created
1712
1713
            // get the cells in the range
1714 17
            $aReferences = Cell::extractAllCellReferencesInRange($pRange);
1715
1716
            // create upper left cell if it does not already exist
1717 17
            $upperLeft = $aReferences[0];
1718 17
            if (!$this->cellExists($upperLeft)) {
1719 13
                $this->getCell($upperLeft)->setValueExplicit(null, Cell\DataType::TYPE_NULL);
1720
            }
1721
1722
            // Blank out the rest of the cells in the range (if they exist)
1723 17
            $count = count($aReferences);
1724 17
            for ($i = 1; $i < $count; ++$i) {
1725 17
                if ($this->cellExists($aReferences[$i])) {
1726 4
                    $this->getCell($aReferences[$i])->setValueExplicit(null, Cell\DataType::TYPE_NULL);
1727
                }
1728
            }
1729
        } else {
1730
            throw new Exception('Merge must be set on a range of cells.');
1731
        }
1732
1733 17
        return $this;
1734
    }
1735
1736
    /**
1737
     * Set merge on a cell range by using numeric cell coordinates.
1738
     *
1739
     * @param int $pColumn1 Numeric column coordinate of the first cell
1740
     * @param int $pRow1 Numeric row coordinate of the first cell
1741
     * @param int $pColumn2 Numeric column coordinate of the last cell
1742
     * @param int $pRow2 Numeric row coordinate of the last cell
1743
     *
1744
     * @throws Exception
1745
     *
1746
     * @return Worksheet
1747
     */
1748
    public function mergeCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
1749
    {
1750
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1751
1752
        return $this->mergeCells($cellRange);
1753
    }
1754
1755
    /**
1756
     * Remove merge on a cell range.
1757
     *
1758
     * @param string $pRange Cell range (e.g. A1:E1)
1759
     *
1760
     * @throws Exception
1761
     *
1762
     * @return Worksheet
1763
     */
1764 12
    public function unmergeCells($pRange = 'A1:A1')
1765
    {
1766
        // Uppercase coordinate
1767 12
        $pRange = strtoupper($pRange);
1768
1769 12
        if (strpos($pRange, ':') !== false) {
1770 12
            if (isset($this->mergeCells[$pRange])) {
1771 12
                unset($this->mergeCells[$pRange]);
1772
            } else {
1773 12
                throw new Exception('Cell range ' . $pRange . ' not known as merged.');
1774
            }
1775
        } else {
1776
            throw new Exception('Merge can only be removed from a range of cells.');
1777
        }
1778
1779 12
        return $this;
1780
    }
1781
1782
    /**
1783
     * Remove merge on a cell range by using numeric cell coordinates.
1784
     *
1785
     * @param int $pColumn1 Numeric column coordinate of the first cell
1786
     * @param int $pRow1 Numeric row coordinate of the first cell
1787
     * @param int $pColumn2 Numeric column coordinate of the last cell
1788
     * @param int $pRow2 Numeric row coordinate of the last cell
1789
     *
1790
     * @throws Exception
1791
     *
1792
     * @return Worksheet
1793
     */
1794
    public function unmergeCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
1795
    {
1796
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1797
1798
        return $this->unmergeCells($cellRange);
1799
    }
1800
1801
    /**
1802
     * Get merge cells array.
1803
     *
1804
     * @return array[]
1805
     */
1806 62
    public function getMergeCells()
1807
    {
1808 62
        return $this->mergeCells;
1809
    }
1810
1811
    /**
1812
     * Set merge cells array for the entire sheet. Use instead mergeCells() to merge
1813
     * a single cell range.
1814
     *
1815
     * @param array
1816
     * @param mixed $pValue
1817
     */
1818 15
    public function setMergeCells($pValue = [])
1819
    {
1820 15
        $this->mergeCells = $pValue;
0 ignored issues
show
Documentation Bug introduced by
It seems like $pValue of type * is incompatible with the declared type array of property $mergeCells.

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...
1821
1822 15
        return $this;
1823
    }
1824
1825
    /**
1826
     * Set protection on a cell range.
1827
     *
1828
     * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
1829
     * @param string $pPassword Password to unlock the protection
1830
     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
1831
     *
1832
     * @throws Exception
1833
     *
1834
     * @return Worksheet
1835
     */
1836 12
    public function protectCells($pRange = 'A1', $pPassword = '', $pAlreadyHashed = false)
1837
    {
1838
        // Uppercase coordinate
1839 12
        $pRange = strtoupper($pRange);
1840
1841 12
        if (!$pAlreadyHashed) {
1842 12
            $pPassword = Shared\PasswordHasher::hashPassword($pPassword);
1843
        }
1844 12
        $this->protectedCells[$pRange] = $pPassword;
1845
1846 12
        return $this;
1847
    }
1848
1849
    /**
1850
     * Set protection on a cell range by using numeric cell coordinates.
1851
     *
1852
     * @param int $pColumn1 Numeric column coordinate of the first cell
1853
     * @param int $pRow1 Numeric row coordinate of the first cell
1854
     * @param int $pColumn2 Numeric column coordinate of the last cell
1855
     * @param int $pRow2 Numeric row coordinate of the last cell
1856
     * @param string $pPassword Password to unlock the protection
1857
     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
1858
     *
1859
     * @throws Exception
1860
     *
1861
     * @return Worksheet
1862
     */
1863
    public function protectCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1, $pPassword = '', $pAlreadyHashed = false)
1864
    {
1865
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1866
1867
        return $this->protectCells($cellRange, $pPassword, $pAlreadyHashed);
1868
    }
1869
1870
    /**
1871
     * Remove protection on a cell range.
1872
     *
1873
     * @param string $pRange Cell (e.g. A1) or cell range (e.g. A1:E1)
1874
     *
1875
     * @throws Exception
1876
     *
1877
     * @return Worksheet
1878
     */
1879 12
    public function unprotectCells($pRange = 'A1')
1880
    {
1881
        // Uppercase coordinate
1882 12
        $pRange = strtoupper($pRange);
1883
1884 12
        if (isset($this->protectedCells[$pRange])) {
1885 12
            unset($this->protectedCells[$pRange]);
1886
        } else {
1887
            throw new Exception('Cell range ' . $pRange . ' not known as protected.');
1888
        }
1889
1890 12
        return $this;
1891
    }
1892
1893
    /**
1894
     * Remove protection on a cell range by using numeric cell coordinates.
1895
     *
1896
     * @param int $pColumn1 Numeric column coordinate of the first cell
1897
     * @param int $pRow1 Numeric row coordinate of the first cell
1898
     * @param int $pColumn2 Numeric column coordinate of the last cell
1899
     * @param int $pRow2 Numeric row coordinate of the last cell
1900
     * @param string $pPassword Password to unlock the protection
1901
     * @param bool $pAlreadyHashed If the password has already been hashed, set this to true
1902
     *
1903
     * @throws Exception
1904
     *
1905
     * @return Worksheet
1906
     */
1907
    public function unprotectCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1, $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...
1908
    {
1909
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1910
1911
        return $this->unprotectCells($cellRange);
1912
    }
1913
1914
    /**
1915
     * Get protected cells.
1916
     *
1917
     * @return array[]
1918
     */
1919 62
    public function getProtectedCells()
1920
    {
1921 62
        return $this->protectedCells;
1922
    }
1923
1924
    /**
1925
     * Get Autofilter.
1926
     *
1927
     * @return Worksheet\AutoFilter
1928
     */
1929 63
    public function getAutoFilter()
1930
    {
1931 63
        return $this->autoFilter;
1932
    }
1933
1934
    /**
1935
     * Set AutoFilter.
1936
     *
1937
     * @param Worksheet\AutoFilter|string $pValue
1938
     *            A simple string containing a Cell range like 'A1:E10' is permitted for backward compatibility
1939
     *
1940
     * @throws Exception
1941
     *
1942
     * @return Worksheet
1943
     */
1944 4
    public function setAutoFilter($pValue)
1945
    {
1946 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...
1947 4
        if (is_string($pValue)) {
1948 4
            $this->autoFilter->setRange($pValue);
1949
        } elseif (is_object($pValue) && ($pValue instanceof Worksheet\AutoFilter)) {
1950
            $this->autoFilter = $pValue;
1951
        }
1952
1953 4
        return $this;
1954
    }
1955
1956
    /**
1957
     * Set Autofilter Range by using numeric cell coordinates.
1958
     *
1959
     * @param int $pColumn1 Numeric column coordinate of the first cell
1960
     * @param int $pRow1 Numeric row coordinate of the first cell
1961
     * @param int $pColumn2 Numeric column coordinate of the second cell
1962
     * @param int $pRow2 Numeric row coordinate of the second cell
1963
     *
1964
     * @throws Exception
1965
     *
1966
     * @return Worksheet
1967
     */
1968
    public function setAutoFilterByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
1969
    {
1970
        return $this->setAutoFilter(
1971
            Cell::stringFromColumnIndex($pColumn1) . $pRow1
1972
            . ':' .
1973
            Cell::stringFromColumnIndex($pColumn2) . $pRow2
1974
        );
1975
    }
1976
1977
    /**
1978
     * Remove autofilter.
1979
     *
1980
     * @return Worksheet
1981
     */
1982
    public function removeAutoFilter()
1983
    {
1984
        $this->autoFilter->setRange(null);
1985
1986
        return $this;
1987
    }
1988
1989
    /**
1990
     * Get Freeze Pane.
1991
     *
1992
     * @return string
1993
     */
1994 62
    public function getFreezePane()
1995
    {
1996 62
        return $this->freezePane;
1997
    }
1998
1999
    /**
2000
     * Freeze Pane.
2001
     *
2002
     * @param string $pCell Cell (i.e. A2)
2003
     *                                    Examples:
2004
     *                                        A2 will freeze the rows above cell A2 (i.e row 1)
2005
     *                                        B1 will freeze the columns to the left of cell B1 (i.e column A)
2006
     *                                        B2 will freeze the rows above and to the left of cell A2
2007
     *                                            (i.e row 1 and column A)
2008
     *
2009
     * @throws Exception
2010
     *
2011
     * @return Worksheet
2012
     */
2013 4
    public function freezePane($pCell = '')
2014
    {
2015
        // Uppercase coordinate
2016 4
        $pCell = strtoupper($pCell);
2017 4
        if (strpos($pCell, ':') === false && strpos($pCell, ',') === false) {
2018 4
            $this->freezePane = $pCell;
2019
        } else {
2020
            throw new Exception('Freeze pane can not be set on a range of cells.');
2021
        }
2022
2023 4
        return $this;
2024
    }
2025
2026
    /**
2027
     * Freeze Pane by using numeric cell coordinates.
2028
     *
2029
     * @param int $pColumn Numeric column coordinate of the cell
2030
     * @param int $pRow Numeric row coordinate of the cell
2031
     *
2032
     * @throws Exception
2033
     *
2034
     * @return Worksheet
2035
     */
2036
    public function freezePaneByColumnAndRow($pColumn = 0, $pRow = 1)
2037
    {
2038
        return $this->freezePane(Cell::stringFromColumnIndex($pColumn) . $pRow);
2039
    }
2040
2041
    /**
2042
     * Unfreeze Pane.
2043
     *
2044
     * @return Worksheet
2045
     */
2046
    public function unfreezePane()
2047
    {
2048
        return $this->freezePane('');
2049
    }
2050
2051
    /**
2052
     * Insert a new row, updating all possible related data.
2053
     *
2054
     * @param int $pBefore Insert before this one
2055
     * @param int $pNumRows Number of rows to insert
2056
     *
2057
     * @throws Exception
2058
     *
2059
     * @return Worksheet
2060
     */
2061 13 View Code Duplication
    public function insertNewRowBefore($pBefore = 1, $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...
2062
    {
2063 13
        if ($pBefore >= 1) {
2064 13
            $objReferenceHelper = ReferenceHelper::getInstance();
2065 13
            $objReferenceHelper->insertNewBefore('A' . $pBefore, 0, $pNumRows, $this);
2066
        } else {
2067
            throw new Exception('Rows can only be inserted before at least row 1.');
2068
        }
2069
2070 13
        return $this;
2071
    }
2072
2073
    /**
2074
     * Insert a new column, updating all possible related data.
2075
     *
2076
     * @param int $pBefore Insert before this one
2077
     * @param int $pNumCols Number of columns to insert
2078
     *
2079
     * @throws Exception
2080
     *
2081
     * @return Worksheet
2082
     */
2083 12 View Code Duplication
    public function insertNewColumnBefore($pBefore = 'A', $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...
2084
    {
2085 12
        if (!is_numeric($pBefore)) {
2086 12
            $objReferenceHelper = ReferenceHelper::getInstance();
2087 12
            $objReferenceHelper->insertNewBefore($pBefore . '1', $pNumCols, 0, $this);
2088
        } else {
2089
            throw new Exception('Column references should not be numeric.');
2090
        }
2091
2092 12
        return $this;
2093
    }
2094
2095
    /**
2096
     * Insert a new column, updating all possible related data.
2097
     *
2098
     * @param int $pBefore Insert before this one (numeric column coordinate of the cell)
2099
     * @param int $pNumCols Number of columns to insert
2100
     *
2101
     * @throws Exception
2102
     *
2103
     * @return Worksheet
2104
     */
2105
    public function insertNewColumnBeforeByIndex($pBefore = 0, $pNumCols = 1)
2106
    {
2107
        if ($pBefore >= 0) {
2108
            return $this->insertNewColumnBefore(Cell::stringFromColumnIndex($pBefore), $pNumCols);
2109
        }
2110
        throw new Exception('Columns can only be inserted before at least column A (0).');
2111
    }
2112
2113
    /**
2114
     * Delete a row, updating all possible related data.
2115
     *
2116
     * @param int $pRow Remove starting with this one
2117
     * @param int $pNumRows Number of rows to remove
2118
     *
2119
     * @throws Exception
2120
     *
2121
     * @return Worksheet
2122
     */
2123 15
    public function removeRow($pRow = 1, $pNumRows = 1)
2124
    {
2125 15
        if ($pRow >= 1) {
2126 15
            $highestRow = $this->getHighestDataRow();
2127 15
            $objReferenceHelper = ReferenceHelper::getInstance();
2128 15
            $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
2129 15
            for ($r = 0; $r < $pNumRows; ++$r) {
2130 15
                $this->getCellCacheController()->removeRow($highestRow);
2131 15
                --$highestRow;
2132
            }
2133
        } else {
2134
            throw new Exception('Rows to be deleted should at least start from row 1.');
2135
        }
2136
2137 15
        return $this;
2138
    }
2139
2140
    /**
2141
     * Remove a column, updating all possible related data.
2142
     *
2143
     * @param string $pColumn Remove starting with this one
2144
     * @param int $pNumCols Number of columns to remove
2145
     *
2146
     * @throws Exception
2147
     *
2148
     * @return Worksheet
2149
     */
2150 12
    public function removeColumn($pColumn = 'A', $pNumCols = 1)
2151
    {
2152 12
        if (!is_numeric($pColumn)) {
2153 12
            $highestColumn = $this->getHighestDataColumn();
2154 12
            $pColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($pColumn) - 1 + $pNumCols);
2155 12
            $objReferenceHelper = ReferenceHelper::getInstance();
2156 12
            $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
2157 12
            for ($c = 0; $c < $pNumCols; ++$c) {
2158 12
                $this->getCellCacheController()->removeColumn($highestColumn);
2159 12
                $highestColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($highestColumn) - 2);
2160
            }
2161
        } else {
2162
            throw new Exception('Column references should not be numeric.');
2163
        }
2164
2165 12
        return $this;
2166
    }
2167
2168
    /**
2169
     * Remove a column, updating all possible related data.
2170
     *
2171
     * @param int $pColumn Remove starting with this one (numeric column coordinate of the cell)
2172
     * @param int $pNumCols Number of columns to remove
2173
     *
2174
     * @throws Exception
2175
     *
2176
     * @return Worksheet
2177
     */
2178
    public function removeColumnByIndex($pColumn = 0, $pNumCols = 1)
2179
    {
2180
        if ($pColumn >= 0) {
2181
            return $this->removeColumn(Cell::stringFromColumnIndex($pColumn), $pNumCols);
2182
        }
2183
        throw new Exception('Columns to be deleted should at least start from column 0');
2184
    }
2185
2186
    /**
2187
     * Show gridlines?
2188
     *
2189
     * @return bool
2190
     */
2191 62
    public function getShowGridlines()
2192
    {
2193 62
        return $this->showGridlines;
2194
    }
2195
2196
    /**
2197
     * Set show gridlines.
2198
     *
2199
     * @param bool $pValue Show gridlines (true/false)
2200
     *
2201
     * @return Worksheet
2202
     */
2203 12
    public function setShowGridlines($pValue = false)
2204
    {
2205 12
        $this->showGridlines = $pValue;
2206
2207 12
        return $this;
2208
    }
2209
2210
    /**
2211
     * Print gridlines?
2212
     *
2213
     * @return bool
2214
     */
2215 58
    public function getPrintGridlines()
2216
    {
2217 58
        return $this->printGridlines;
2218
    }
2219
2220
    /**
2221
     * Set print gridlines.
2222
     *
2223
     * @param bool $pValue Print gridlines (true/false)
2224
     *
2225
     * @return Worksheet
2226
     */
2227 4
    public function setPrintGridlines($pValue = false)
2228
    {
2229 4
        $this->printGridlines = $pValue;
2230
2231 4
        return $this;
2232
    }
2233
2234
    /**
2235
     * Show row and column headers?
2236
     *
2237
     * @return bool
2238
     */
2239 58
    public function getShowRowColHeaders()
2240
    {
2241 58
        return $this->showRowColHeaders;
2242
    }
2243
2244
    /**
2245
     * Set show row and column headers.
2246
     *
2247
     * @param bool $pValue Show row and column headers (true/false)
2248
     *
2249
     * @return Worksheet
2250
     */
2251 9
    public function setShowRowColHeaders($pValue = false)
2252
    {
2253 9
        $this->showRowColHeaders = $pValue;
2254
2255 9
        return $this;
2256
    }
2257
2258
    /**
2259
     * Show summary below? (Row/Column outlining).
2260
     *
2261
     * @return bool
2262
     */
2263 58
    public function getShowSummaryBelow()
2264
    {
2265 58
        return $this->showSummaryBelow;
2266
    }
2267
2268
    /**
2269
     * Set show summary below.
2270
     *
2271
     * @param bool $pValue Show summary below (true/false)
2272
     *
2273
     * @return Worksheet
2274
     */
2275 9
    public function setShowSummaryBelow($pValue = true)
2276
    {
2277 9
        $this->showSummaryBelow = $pValue;
2278
2279 9
        return $this;
2280
    }
2281
2282
    /**
2283
     * Show summary right? (Row/Column outlining).
2284
     *
2285
     * @return bool
2286
     */
2287 58
    public function getShowSummaryRight()
2288
    {
2289 58
        return $this->showSummaryRight;
2290
    }
2291
2292
    /**
2293
     * Set show summary right.
2294
     *
2295
     * @param bool $pValue Show summary right (true/false)
2296
     *
2297
     * @return Worksheet
2298
     */
2299 9
    public function setShowSummaryRight($pValue = true)
2300
    {
2301 9
        $this->showSummaryRight = $pValue;
2302
2303 9
        return $this;
2304
    }
2305
2306
    /**
2307
     * Get comments.
2308
     *
2309
     * @return Comment[]
2310
     */
2311 63
    public function getComments()
2312
    {
2313 63
        return $this->comments;
2314
    }
2315
2316
    /**
2317
     * Set comments array for the entire sheet.
2318
     *
2319
     * @param array of Comment
2320
     * @param mixed $pValue
2321
     *
2322
     * @return Worksheet
2323
     */
2324 15
    public function setComments($pValue = [])
2325
    {
2326 15
        $this->comments = $pValue;
0 ignored issues
show
Documentation Bug introduced by
It seems like $pValue of type * is incompatible with the declared type array<integer,object<Php...hpSpreadsheet\Comment>> of property $comments.

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...
2327
2328 15
        return $this;
2329
    }
2330
2331
    /**
2332
     * Get comment for cell.
2333
     *
2334
     * @param string $pCellCoordinate Cell coordinate to get comment for
2335
     *
2336
     * @throws Exception
2337
     *
2338
     * @return Comment
2339
     */
2340 20
    public function getComment($pCellCoordinate = 'A1')
2341
    {
2342
        // Uppercase coordinate
2343 20
        $pCellCoordinate = strtoupper($pCellCoordinate);
2344
2345 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...
2346
            throw new Exception('Cell coordinate string can not be a range of cells.');
2347 20
        } elseif (strpos($pCellCoordinate, '$') !== false) {
2348
            throw new Exception('Cell coordinate string must not be absolute.');
2349 20
        } elseif ($pCellCoordinate == '') {
2350
            throw new Exception('Cell coordinate can not be zero-length string.');
2351
        }
2352
2353
        // Check if we already have a comment for this cell.
2354 20
        if (isset($this->comments[$pCellCoordinate])) {
2355 13
            return $this->comments[$pCellCoordinate];
2356
        }
2357
2358
        // If not, create a new comment.
2359 20
        $newComment = new Comment();
2360 20
        $this->comments[$pCellCoordinate] = $newComment;
2361
2362 20
        return $newComment;
2363
    }
2364
2365
    /**
2366
     * Get comment for cell by using numeric cell coordinates.
2367
     *
2368
     * @param int $pColumn Numeric column coordinate of the cell
2369
     * @param int $pRow Numeric row coordinate of the cell
2370
     *
2371
     * @return Comment
2372
     */
2373 2
    public function getCommentByColumnAndRow($pColumn = 0, $pRow = 1)
2374
    {
2375 2
        return $this->getComment(Cell::stringFromColumnIndex($pColumn) . $pRow);
2376
    }
2377
2378
    /**
2379
     * Get active cell.
2380
     *
2381
     * @return string Example: 'A1'
2382
     */
2383 58
    public function getActiveCell()
2384
    {
2385 58
        return $this->activeCell;
2386
    }
2387
2388
    /**
2389
     * Get selected cells.
2390
     *
2391
     * @return string
2392
     */
2393 55
    public function getSelectedCells()
2394
    {
2395 55
        return $this->selectedCells;
2396
    }
2397
2398
    /**
2399
     * Selected cell.
2400
     *
2401
     * @param string $pCoordinate Cell (i.e. A1)
2402
     *
2403
     * @return Worksheet
2404
     */
2405
    public function setSelectedCell($pCoordinate = 'A1')
2406
    {
2407
        return $this->setSelectedCells($pCoordinate);
2408
    }
2409
2410
    /**
2411
     * Select a range of cells.
2412
     *
2413
     * @param string $pCoordinate Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6'
2414
     *
2415
     * @throws Exception
2416
     *
2417
     * @return Worksheet
2418
     */
2419 48
    public function setSelectedCells($pCoordinate = 'A1')
2420
    {
2421
        // Uppercase coordinate
2422 48
        $pCoordinate = strtoupper($pCoordinate);
2423
2424
        // Convert 'A' to 'A:A'
2425 48
        $pCoordinate = preg_replace('/^([A-Z]+)$/', '${1}:${1}', $pCoordinate);
2426
2427
        // Convert '1' to '1:1'
2428 48
        $pCoordinate = preg_replace('/^([0-9]+)$/', '${1}:${1}', $pCoordinate);
2429
2430
        // Convert 'A:C' to 'A1:C1048576'
2431 48
        $pCoordinate = preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $pCoordinate);
2432
2433
        // Convert '1:3' to 'A1:XFD3'
2434 48
        $pCoordinate = preg_replace('/^([0-9]+):([0-9]+)$/', 'A${1}:XFD${2}', $pCoordinate);
2435
2436 48
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
2437 26
            list($first) = Cell::splitRange($pCoordinate);
2438 26
            $this->activeCell = $first[0];
2439
        } else {
2440 41
            $this->activeCell = $pCoordinate;
2441
        }
2442 48
        $this->selectedCells = $pCoordinate;
2443
2444 48
        return $this;
2445
    }
2446
2447
    /**
2448
     * Selected cell by using numeric cell coordinates.
2449
     *
2450
     * @param int $pColumn Numeric column coordinate of the cell
2451
     * @param int $pRow Numeric row coordinate of the cell
2452
     *
2453
     * @throws Exception
2454
     *
2455
     * @return Worksheet
2456
     */
2457
    public function setSelectedCellByColumnAndRow($pColumn = 0, $pRow = 1)
2458
    {
2459
        return $this->setSelectedCells(Cell::stringFromColumnIndex($pColumn) . $pRow);
2460
    }
2461
2462
    /**
2463
     * Get right-to-left.
2464
     *
2465
     * @return bool
2466
     */
2467 58
    public function getRightToLeft()
2468
    {
2469 58
        return $this->rightToLeft;
2470
    }
2471
2472
    /**
2473
     * Set right-to-left.
2474
     *
2475
     * @param bool $value Right-to-left true/false
2476
     *
2477
     * @return Worksheet
2478
     */
2479 4
    public function setRightToLeft($value = false)
2480
    {
2481 4
        $this->rightToLeft = $value;
2482
2483 4
        return $this;
2484
    }
2485
2486
    /**
2487
     * Fill worksheet from values in array.
2488
     *
2489
     * @param array $source Source array
2490
     * @param mixed $nullValue Value in source array that stands for blank cell
2491
     * @param string $startCell Insert array starting from this cell address as the top left coordinate
2492
     * @param bool $strictNullComparison Apply strict comparison when testing for null values in the array
2493
     *
2494
     * @throws Exception
2495
     *
2496
     * @return Worksheet
2497
     */
2498 17
    public function fromArray($source = null, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
2499
    {
2500 17
        if (is_array($source)) {
2501
            //    Convert a 1-D array to 2-D (for ease of looping)
2502 17
            if (!is_array(end($source))) {
2503 3
                $source = [$source];
2504
            }
2505
2506
            // start coordinate
2507 17
            list($startColumn, $startRow) = Cell::coordinateFromString($startCell);
2508
2509
            // Loop through $source
2510 17
            foreach ($source as $rowData) {
2511 17
                $currentColumn = $startColumn;
2512 17
                foreach ($rowData as $cellValue) {
2513 17
                    if ($strictNullComparison) {
2514 1
                        if ($cellValue !== $nullValue) {
2515
                            // Set cell value
2516 1
                            $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2517
                        }
2518
                    } else {
2519 16
                        if ($cellValue != $nullValue) {
2520
                            // Set cell value
2521 16
                            $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2522
                        }
2523
                    }
2524 17
                    ++$currentColumn;
2525
                }
2526 17
                ++$startRow;
2527
            }
2528
        } else {
2529
            throw new Exception('Parameter $source should be an array.');
2530
        }
2531
2532 17
        return $this;
2533
    }
2534
2535
    /**
2536
     * Create array from a range of cells.
2537
     *
2538
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
2539
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2540
     * @param bool $calculateFormulas Should formulas be calculated?
2541
     * @param bool $formatData Should formatting be applied to cell values?
2542
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2543
     *                               True - Return rows and columns indexed by their actual row and column IDs
2544
     *
2545
     * @return array
2546
     */
2547 2
    public function rangeToArray($pRange = 'A1', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2548
    {
2549
        // Returnvalue
2550 2
        $returnValue = [];
2551
        //    Identify the range that we need to extract from the worksheet
2552 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange);
2553 2
        $minCol = Cell::stringFromColumnIndex($rangeStart[0] - 1);
2554 2
        $minRow = $rangeStart[1];
2555 2
        $maxCol = Cell::stringFromColumnIndex($rangeEnd[0] - 1);
2556 2
        $maxRow = $rangeEnd[1];
2557
2558 2
        ++$maxCol;
2559
        // Loop through rows
2560 2
        $r = -1;
2561 2
        for ($row = $minRow; $row <= $maxRow; ++$row) {
2562 2
            $rRef = ($returnCellRef) ? $row : ++$r;
2563 2
            $c = -1;
2564
            // Loop through columns in the current row
2565 2
            for ($col = $minCol; $col != $maxCol; ++$col) {
2566 2
                $cRef = ($returnCellRef) ? $col : ++$c;
2567
                //    Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
2568
                //        so we test and retrieve directly against cellCollection
2569 2
                if ($this->cellCollection->isDataSet($col . $row)) {
2570
                    // Cell exists
2571 2
                    $cell = $this->cellCollection->getCacheData($col . $row);
2572 2
                    if ($cell->getValue() !== null) {
2573 2
                        if ($cell->getValue() instanceof RichText) {
2574 2
                            $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
2575
                        } else {
2576 2
                            if ($calculateFormulas) {
2577 2
                                $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
2578
                            } else {
2579
                                $returnValue[$rRef][$cRef] = $cell->getValue();
2580
                            }
2581
                        }
2582
2583 2
                        if ($formatData) {
2584 2
                            $style = $this->parent->getCellXfByIndex($cell->getXfIndex());
2585 2
                            $returnValue[$rRef][$cRef] = Style\NumberFormat::toFormattedString(
2586 2
                                $returnValue[$rRef][$cRef],
2587 2
                                ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : Style\NumberFormat::FORMAT_GENERAL
2588
                            );
2589
                        }
2590
                    } else {
2591
                        // Cell holds a NULL
2592 2
                        $returnValue[$rRef][$cRef] = $nullValue;
2593
                    }
2594
                } else {
2595
                    // Cell doesn't exist
2596 1
                    $returnValue[$rRef][$cRef] = $nullValue;
2597
                }
2598
            }
2599
        }
2600
2601
        // Return
2602 2
        return $returnValue;
2603
    }
2604
2605
    /**
2606
     * Create array from a range of cells.
2607
     *
2608
     * @param string $pNamedRange Name of the Named Range
2609
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2610
     * @param bool $calculateFormulas Should formulas be calculated?
2611
     * @param bool $formatData Should formatting be applied to cell values?
2612
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2613
     *                                True - Return rows and columns indexed by their actual row and column IDs
2614
     *
2615
     * @throws Exception
2616
     *
2617
     * @return array
2618
     */
2619
    public function namedRangeToArray($pNamedRange = '', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2620
    {
2621
        $namedRange = NamedRange::resolveRange($pNamedRange, $this);
2622
        if ($namedRange !== null) {
2623
            $pWorkSheet = $namedRange->getWorksheet();
2624
            $pCellRange = $namedRange->getRange();
2625
2626
            return $pWorkSheet->rangeToArray($pCellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
2627
        }
2628
2629
        throw new Exception('Named Range ' . $pNamedRange . ' does not exist.');
2630
    }
2631
2632
    /**
2633
     * Create array from worksheet.
2634
     *
2635
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2636
     * @param bool $calculateFormulas Should formulas be calculated?
2637
     * @param bool $formatData Should formatting be applied to cell values?
2638
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2639
     *                               True - Return rows and columns indexed by their actual row and column IDs
2640
     *
2641
     * @return array
2642
     */
2643
    public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2644
    {
2645
        // Garbage collect...
2646
        $this->garbageCollect();
2647
2648
        //    Identify the range that we need to extract from the worksheet
2649
        $maxCol = $this->getHighestColumn();
2650
        $maxRow = $this->getHighestRow();
2651
        // Return
2652
        return $this->rangeToArray('A1:' . $maxCol . $maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
2653
    }
2654
2655
    /**
2656
     * Get row iterator.
2657
     *
2658
     * @param int $startRow The row number at which to start iterating
2659
     * @param int $endRow The row number at which to stop iterating
2660
     *
2661
     * @return Worksheet\RowIterator
2662
     */
2663 4
    public function getRowIterator($startRow = 1, $endRow = null)
2664
    {
2665 4
        return new Worksheet\RowIterator($this, $startRow, $endRow);
2666
    }
2667
2668
    /**
2669
     * Get column iterator.
2670
     *
2671
     * @param string $startColumn The column address at which to start iterating
2672
     * @param string $endColumn The column address at which to stop iterating
2673
     *
2674
     * @return Worksheet\ColumnIterator
2675
     */
2676
    public function getColumnIterator($startColumn = 'A', $endColumn = null)
2677
    {
2678
        return new Worksheet\ColumnIterator($this, $startColumn, $endColumn);
2679
    }
2680
2681
    /**
2682
     * Run PhpSpreadsheet garabage collector.
2683
     *
2684
     * @return Worksheet
2685
     */
2686 62
    public function garbageCollect()
2687
    {
2688
        // Flush cache
2689 62
        $this->cellCollection->getCacheData('A1');
2690
2691
        // Lookup highest column and highest row if cells are cleaned
2692 62
        $colRow = $this->cellCollection->getHighestRowAndColumn();
2693 62
        $highestRow = $colRow['row'];
2694 62
        $highestColumn = Cell::columnIndexFromString($colRow['column']);
2695
2696
        // Loop through column dimensions
2697 62
        foreach ($this->columnDimensions as $dimension) {
2698 27
            $highestColumn = max($highestColumn, Cell::columnIndexFromString($dimension->getColumnIndex()));
2699
        }
2700
2701
        // Loop through row dimensions
2702 62
        foreach ($this->rowDimensions as $dimension) {
2703 39
            $highestRow = max($highestRow, $dimension->getRowIndex());
2704
        }
2705
2706
        // Cache values
2707 62
        if ($highestColumn < 0) {
2708
            $this->cachedHighestColumn = 'A';
2709
        } else {
2710 62
            $this->cachedHighestColumn = Cell::stringFromColumnIndex(--$highestColumn);
2711
        }
2712 62
        $this->cachedHighestRow = $highestRow;
2713
2714
        // Return
2715 62
        return $this;
2716
    }
2717
2718
    /**
2719
     * Get hash code.
2720
     *
2721
     * @return string Hash code
2722
     */
2723 70
    public function getHashCode()
2724
    {
2725 70
        if ($this->dirty) {
2726 70
            $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__);
2727 70
            $this->dirty = false;
2728
        }
2729
2730 70
        return $this->hash;
2731
    }
2732
2733
    /**
2734
     * Extract worksheet title from range.
2735
     *
2736
     * Example: extractSheetTitle("testSheet!A1") ==> 'A1'
2737
     * Example: extractSheetTitle("'testSheet 1'!A1", true) ==> array('testSheet 1', 'A1');
2738
     *
2739
     * @param string $pRange Range to extract title from
2740
     * @param bool $returnRange Return range? (see example)
2741
     *
2742
     * @return mixed
2743
     */
2744 1
    public static function extractSheetTitle($pRange, $returnRange = false)
2745
    {
2746
        // Sheet title included?
2747 1
        if (($sep = strpos($pRange, '!')) === false) {
2748
            return '';
2749
        }
2750
2751 1
        if ($returnRange) {
2752 1
            return [trim(substr($pRange, 0, $sep), "'"), substr($pRange, $sep + 1)];
2753
        }
2754
2755
        return substr($pRange, $sep + 1);
2756
    }
2757
2758
    /**
2759
     * Get hyperlink.
2760
     *
2761
     * @param string $pCellCoordinate Cell coordinate to get hyperlink for
2762
     */
2763 19 View Code Duplication
    public function getHyperlink($pCellCoordinate = 'A1')
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...
2764
    {
2765
        // return hyperlink if we already have one
2766 19
        if (isset($this->hyperlinkCollection[$pCellCoordinate])) {
2767 14
            return $this->hyperlinkCollection[$pCellCoordinate];
2768
        }
2769
2770
        // else create hyperlink
2771 19
        $this->hyperlinkCollection[$pCellCoordinate] = new Cell\Hyperlink();
2772
2773 19
        return $this->hyperlinkCollection[$pCellCoordinate];
2774
    }
2775
2776
    /**
2777
     * Set hyperlnk.
2778
     *
2779
     * @param string $pCellCoordinate Cell coordinate to insert hyperlink
2780
     * @param Cell\Hyperlink $pHyperlink
2781
     *
2782
     * @return Worksheet
2783
     */
2784 13
    public function setHyperlink($pCellCoordinate = 'A1', Cell\Hyperlink $pHyperlink = null)
2785
    {
2786 13
        if ($pHyperlink === null) {
2787 13
            unset($this->hyperlinkCollection[$pCellCoordinate]);
2788
        } else {
2789 13
            $this->hyperlinkCollection[$pCellCoordinate] = $pHyperlink;
2790
        }
2791
2792 13
        return $this;
2793
    }
2794
2795
    /**
2796
     * Hyperlink at a specific coordinate exists?
2797
     *
2798
     * @param string $pCoordinate
2799
     *
2800
     * @return bool
2801
     */
2802 6
    public function hyperlinkExists($pCoordinate = 'A1')
2803
    {
2804 6
        return isset($this->hyperlinkCollection[$pCoordinate]);
2805
    }
2806
2807
    /**
2808
     * Get collection of hyperlinks.
2809
     *
2810
     * @return Cell\Hyperlink[]
2811
     */
2812 62
    public function getHyperlinkCollection()
2813
    {
2814 62
        return $this->hyperlinkCollection;
2815
    }
2816
2817
    /**
2818
     * Get data validation.
2819
     *
2820
     * @param string $pCellCoordinate Cell coordinate to get data validation for
2821
     */
2822 2 View Code Duplication
    public function getDataValidation($pCellCoordinate = 'A1')
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...
2823
    {
2824
        // return data validation if we already have one
2825 2
        if (isset($this->dataValidationCollection[$pCellCoordinate])) {
2826
            return $this->dataValidationCollection[$pCellCoordinate];
2827
        }
2828
2829
        // else create data validation
2830 2
        $this->dataValidationCollection[$pCellCoordinate] = new Cell\DataValidation();
2831
2832 2
        return $this->dataValidationCollection[$pCellCoordinate];
2833
    }
2834
2835
    /**
2836
     * Set data validation.
2837
     *
2838
     * @param string $pCellCoordinate Cell coordinate to insert data validation
2839
     * @param Cell\DataValidation $pDataValidation
2840
     *
2841
     * @return Worksheet
2842
     */
2843
    public function setDataValidation($pCellCoordinate = 'A1', Cell\DataValidation $pDataValidation = null)
2844
    {
2845
        if ($pDataValidation === null) {
2846
            unset($this->dataValidationCollection[$pCellCoordinate]);
2847
        } else {
2848
            $this->dataValidationCollection[$pCellCoordinate] = $pDataValidation;
2849
        }
2850
2851
        return $this;
2852
    }
2853
2854
    /**
2855
     * Data validation at a specific coordinate exists?
2856
     *
2857
     * @param string $pCoordinate
2858
     *
2859
     * @return bool
2860
     */
2861
    public function dataValidationExists($pCoordinate = 'A1')
2862
    {
2863
        return isset($this->dataValidationCollection[$pCoordinate]);
2864
    }
2865
2866
    /**
2867
     * Get collection of data validations.
2868
     *
2869
     * @return Cell\DataValidation[]
2870
     */
2871 62
    public function getDataValidationCollection()
2872
    {
2873 62
        return $this->dataValidationCollection;
2874
    }
2875
2876
    /**
2877
     * Accepts a range, returning it as a range that falls within the current highest row and column of the worksheet.
2878
     *
2879
     * @param string $range
2880
     *
2881
     * @return string Adjusted range value
2882
     */
2883
    public function shrinkRangeToFit($range)
2884
    {
2885
        $maxCol = $this->getHighestColumn();
2886
        $maxRow = $this->getHighestRow();
2887
        $maxCol = Cell::columnIndexFromString($maxCol);
2888
2889
        $rangeBlocks = explode(' ', $range);
2890
        foreach ($rangeBlocks as &$rangeSet) {
2891
            $rangeBoundaries = Cell::getRangeBoundaries($rangeSet);
2892
2893 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...
2894
                $rangeBoundaries[0][0] = Cell::stringFromColumnIndex($maxCol);
2895
            }
2896
            if ($rangeBoundaries[0][1] > $maxRow) {
2897
                $rangeBoundaries[0][1] = $maxRow;
2898
            }
2899 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...
2900
                $rangeBoundaries[1][0] = Cell::stringFromColumnIndex($maxCol);
2901
            }
2902
            if ($rangeBoundaries[1][1] > $maxRow) {
2903
                $rangeBoundaries[1][1] = $maxRow;
2904
            }
2905
            $rangeSet = $rangeBoundaries[0][0] . $rangeBoundaries[0][1] . ':' . $rangeBoundaries[1][0] . $rangeBoundaries[1][1];
2906
        }
2907
        unset($rangeSet);
2908
        $stRange = implode(' ', $rangeBlocks);
2909
2910
        return $stRange;
2911
    }
2912
2913
    /**
2914
     * Get tab color.
2915
     *
2916
     * @return Style\Color
2917
     */
2918 12
    public function getTabColor()
2919
    {
2920 12
        if ($this->tabColor === null) {
2921 12
            $this->tabColor = new Style\Color();
2922
        }
2923
2924 12
        return $this->tabColor;
2925
    }
2926
2927
    /**
2928
     * Reset tab color.
2929
     *
2930
     * @return Worksheet
2931
     */
2932
    public function resetTabColor()
2933
    {
2934
        $this->tabColor = null;
2935
        unset($this->tabColor);
2936
2937
        return $this;
2938
    }
2939
2940
    /**
2941
     * Tab color set?
2942
     *
2943
     * @return bool
2944
     */
2945 58
    public function isTabColorSet()
2946
    {
2947 58
        return $this->tabColor !== null;
2948
    }
2949
2950
    /**
2951
     * Copy worksheet (!= clone!).
2952
     *
2953
     * @return Worksheet
2954
     */
2955
    public function copy()
2956
    {
2957
        $copied = clone $this;
2958
2959
        return $copied;
2960
    }
2961
2962
    /**
2963
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
2964
     */
2965 1
    public function __clone()
2966
    {
2967 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...
2968 1
            if ($key == 'parent') {
2969 1
                continue;
2970
            }
2971
2972 1
            if (is_object($val) || (is_array($val))) {
2973 1
                if ($key == 'cellCollection') {
2974 1
                    $newCollection = clone $this->cellCollection;
2975 1
                    $newCollection->copyCellCollection($this);
2976 1
                    $this->cellCollection = $newCollection;
2977 1
                } elseif ($key == 'drawingCollection') {
2978 1
                    $newCollection = clone $this->drawingCollection;
2979 1
                    $this->drawingCollection = $newCollection;
0 ignored issues
show
Documentation Bug introduced by
It seems like $newCollection can also be of type object. However, the property $drawingCollection is declared as type array<integer,object<Php...Worksheet\BaseDrawing>>. 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...
2980 1
                } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof Worksheet\AutoFilter)) {
2981 1
                    $newAutoFilter = clone $this->autoFilter;
2982 1
                    $this->autoFilter = $newAutoFilter;
2983 1
                    $this->autoFilter->setParent($this);
2984
                } else {
2985 1
                    $this->{$key} = unserialize(serialize($val));
2986
                }
2987
            }
2988
        }
2989 1
    }
2990
2991
    /**
2992
     * Define the code name of the sheet.
2993
     *
2994
     * @param null|string Same rule as Title minus space not allowed (but, like Excel, change silently space to underscore)
2995
     * @param null|mixed $pValue
2996
     *
2997
     * @throws Exception
2998
     *
2999
     * @return objWorksheet
3000
     */
3001 81
    public function setCodeName($pValue = null)
3002
    {
3003
        // Is this a 'rename' or not?
3004 81
        if ($this->getCodeName() == $pValue) {
3005
            return $this;
3006
        }
3007 81
        $pValue = str_replace(' ', '_', $pValue); //Excel does this automatically without flinching, we are doing the same
3008
        // Syntax check
3009
        // throw an exception if not valid
3010 81
        self::checkSheetCodeName($pValue);
3011
3012
        // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_'
3013
3014 81
        if ($this->getParent()) {
3015
            // Is there already such sheet name?
3016 73
            if ($this->getParent()->sheetCodeNameExists($pValue)) {
3017
                // Use name, but append with lowest possible integer
3018
3019 26
                if (Shared\StringHelper::countCharacters($pValue) > 29) {
3020
                    $pValue = Shared\StringHelper::substring($pValue, 0, 29);
3021
                }
3022 26
                $i = 1;
3023 26 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...
3024 2
                    ++$i;
3025 2
                    if ($i == 10) {
3026
                        if (Shared\StringHelper::countCharacters($pValue) > 28) {
3027
                            $pValue = Shared\StringHelper::substring($pValue, 0, 28);
3028
                        }
3029 2
                    } elseif ($i == 100) {
3030
                        if (Shared\StringHelper::countCharacters($pValue) > 27) {
3031
                            $pValue = Shared\StringHelper::substring($pValue, 0, 27);
3032
                        }
3033
                    }
3034
                }
3035
3036 26
                $pValue = $pValue . '_' . $i; // ok, we have a valid name
3037
                //codeName is'nt used in formula : no need to call for an update
3038
                //return $this->setTitle($altTitle, $updateFormulaCellReferences);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
3039
            }
3040
        }
3041
3042 81
        $this->codeName = $pValue;
3043
3044 81
        return $this;
3045
    }
3046
3047
    /**
3048
     * Return the code name of the sheet.
3049
     *
3050
     * @return null|string
3051
     */
3052 81
    public function getCodeName()
3053
    {
3054 81
        return $this->codeName;
3055
    }
3056
3057
    /**
3058
     * Sheet has a code name ?
3059
     *
3060
     * @return bool
3061
     */
3062
    public function hasCodeName()
3063
    {
3064
        return !(is_null($this->codeName));
3065
    }
3066
}
3067