Completed
Push — develop ( 8cf911...93ccf7 )
by Adrien
33:03
created

Worksheet::namedRangeToArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
cc 2
eloc 7
c 0
b 0
f 0
nc 2
nop 5
dl 0
loc 12
ccs 0
cts 7
cp 0
crap 6
rs 9.4285
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
 * @copyright  Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
24
 * @license    http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
25
 * @version    ##VERSION##, ##DATE##
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 70
    public function __construct(Spreadsheet $parent = null, $pTitle = 'Worksheet')
333
    {
334
        // Set parent and title
335 70
        $this->parent = $parent;
336 70
        $this->setTitle($pTitle, false);
337
        // setTitle can change $pTitle
338 70
        $this->setCodeName($this->getTitle());
339 70
        $this->setSheetState(self::SHEETSTATE_VISIBLE);
340
341 70
        $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 70
        $this->pageSetup = new Worksheet\PageSetup();
344
        // Set page margins
345 70
        $this->pageMargins = new Worksheet\PageMargins();
346
        // Set page header/footer
347 70
        $this->headerFooter = new Worksheet\HeaderFooter();
348
        // Set sheet view
349 70
        $this->sheetView = new Worksheet\SheetView();
350
        // Drawing collection
351 70
        $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 70
        $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 70
        $this->protection = new Worksheet\Protection();
356
        // Default row dimension
357 70
        $this->defaultRowDimension = new Worksheet\RowDimension(null);
358
        // Default column dimension
359 70
        $this->defaultColumnDimension = new Worksheet\ColumnDimension(null);
360 70
        $this->autoFilter = new Worksheet\AutoFilter(null, $this);
361 70
    }
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 62
    public function getCellCacheController()
393
    {
394 62
        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
     * @throws Exception
412
     * @return string The valid string
413
     */
414 70
    private static function checkSheetCodeName($pValue)
415
    {
416 70
        $CharCount = Shared\StringHelper::countCharacters($pValue);
417 70
        if ($CharCount == 0) {
418
            throw new Exception('Sheet code name cannot be empty.');
419
        }
420
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ] and  first and last characters cannot be a "'"
421 70
        if ((str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) ||
422 70
            (Shared\StringHelper::substring($pValue, -1, 1) == '\'') ||
423 70
            (Shared\StringHelper::substring($pValue, 0, 1) == '\'')) {
424
            throw new Exception('Invalid character found in sheet code name');
425
        }
426
427
        // Maximum 31 characters allowed for sheet title
428 70
        if ($CharCount > 31) {
429
            throw new Exception('Maximum 31 characters allowed in sheet code name.');
430
        }
431
432 70
        return $pValue;
433
    }
434
435
    /**
436
     * Check sheet title for valid Excel syntax
437
     *
438
     * @param string $pValue The string to check
439
     * @throws Exception
440
     * @return string The valid string
441
     */
442 70
    private static function checkSheetTitle($pValue)
443
    {
444
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ]
445 70
        if (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) {
446
            throw new Exception('Invalid character found in sheet title');
447
        }
448
449
        // Maximum 31 characters allowed for sheet title
450 70
        if (Shared\StringHelper::countCharacters($pValue) > 31) {
451
            throw new Exception('Maximum 31 characters allowed in sheet title.');
452
        }
453
454 70
        return $pValue;
455
    }
456
457
    /**
458
     * Get collection of cells
459
     *
460
     * @param bool $pSorted Also sort the cell collection?
461
     * @return Cell[]
462
     */
463 60
    public function getCellCollection($pSorted = true)
464
    {
465 60
        if ($pSorted) {
466
            // Re-order cell collection
467 60
            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...
468
        }
469 59
        if ($this->cellCollection !== null) {
470 59
            return $this->cellCollection->getCellList();
471
        }
472
473
        return [];
474
    }
475
476
    /**
477
     * Sort collection of cells
478
     *
479
     * @return Worksheet
480
     */
481 60
    public function sortCellCollection()
482
    {
483 60
        if ($this->cellCollection !== null) {
484 60
            return $this->cellCollection->getSortedCellList();
485
        }
486
487
        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...
488
    }
489
490
    /**
491
     * Get collection of row dimensions
492
     *
493
     * @return Worksheet\RowDimension[]
494
     */
495 59
    public function getRowDimensions()
496
    {
497 59
        return $this->rowDimensions;
498
    }
499
500
    /**
501
     * Get default row dimension
502
     *
503
     * @return Worksheet\RowDimension
504
     */
505 59
    public function getDefaultRowDimension()
506
    {
507 59
        return $this->defaultRowDimension;
508
    }
509
510
    /**
511
     * Get collection of column dimensions
512
     *
513
     * @return Worksheet\ColumnDimension[]
514
     */
515 59
    public function getColumnDimensions()
516
    {
517 59
        return $this->columnDimensions;
518
    }
519
520
    /**
521
     * Get default column dimension
522
     *
523
     * @return Worksheet\ColumnDimension
524
     */
525 58
    public function getDefaultColumnDimension()
526
    {
527 58
        return $this->defaultColumnDimension;
528
    }
529
530
    /**
531
     * Get collection of drawings
532
     *
533
     * @return Worksheet\BaseDrawing[]
534
     */
535 59
    public function getDrawingCollection()
536
    {
537 59
        return $this->drawingCollection;
538
    }
539
540
    /**
541
     * Get collection of charts
542
     *
543
     * @return Chart[]
544
     */
545 14
    public function getChartCollection()
546
    {
547 14
        return $this->chartCollection;
548
    }
549
550
    /**
551
     * Add chart
552
     *
553
     * @param Chart $pChart
554
     * @param int|null $iChartIndex Index where chart should go (0,1,..., or null for last)
555
     * @return Chart
556
     */
557 14
    public function addChart(Chart $pChart = null, $iChartIndex = null)
558
    {
559 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...
560 14
        if (is_null($iChartIndex)) {
561 14
            $this->chartCollection[] = $pChart;
562
        } else {
563
            // Insert the chart at the requested index
564
            array_splice($this->chartCollection, $iChartIndex, 0, [$pChart]);
565
        }
566
567 14
        return $pChart;
568
    }
569
570
    /**
571
     * Return the count of charts on this worksheet
572
     *
573
     * @return int        The number of charts
574
     */
575 14
    public function getChartCount()
576
    {
577 14
        return count($this->chartCollection);
578
    }
579
580
    /**
581
     * Get a chart by its index position
582
     *
583
     * @param string $index Chart index position
584
     * @throws Exception
585
     * @return false|Chart
586
     */
587 13
    public function getChartByIndex($index = null)
588
    {
589 13
        $chartCount = count($this->chartCollection);
590 13
        if ($chartCount == 0) {
591
            return false;
592
        }
593 13
        if (is_null($index)) {
594
            $index = --$chartCount;
595
        }
596 13
        if (!isset($this->chartCollection[$index])) {
597
            return false;
598
        }
599
600 13
        return $this->chartCollection[$index];
601
    }
602
603
    /**
604
     * Return an array of the names of charts on this worksheet
605
     *
606
     * @throws Exception
607
     * @return string[] The names of charts
608
     */
609 1
    public function getChartNames()
610
    {
611 1
        $chartNames = [];
612 1
        foreach ($this->chartCollection as $chart) {
613 1
            $chartNames[] = $chart->getName();
614
        }
615
616 1
        return $chartNames;
617
    }
618
619
    /**
620
     * Get a chart by name
621
     *
622
     * @param string $chartName Chart name
623
     * @throws Exception
624
     * @return false|Chart
625
     */
626 1
    public function getChartByName($chartName = '')
627
    {
628 1
        $chartCount = count($this->chartCollection);
629 1
        if ($chartCount == 0) {
630
            return false;
631
        }
632 1
        foreach ($this->chartCollection as $index => $chart) {
633 1
            if ($chart->getName() == $chartName) {
634 1
                return $this->chartCollection[$index];
635
            }
636
        }
637
638
        return false;
639
    }
640
641
    /**
642
     * Refresh column dimensions
643
     *
644
     * @return Worksheet
645
     */
646 12
    public function refreshColumnDimensions()
647
    {
648 12
        $currentColumnDimensions = $this->getColumnDimensions();
649 12
        $newColumnDimensions = [];
650
651 12
        foreach ($currentColumnDimensions as $objColumnDimension) {
652 12
            $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
653
        }
654
655 12
        $this->columnDimensions = $newColumnDimensions;
656
657 12
        return $this;
658
    }
659
660
    /**
661
     * Refresh row dimensions
662
     *
663
     * @return Worksheet
664
     */
665 2
    public function refreshRowDimensions()
666
    {
667 2
        $currentRowDimensions = $this->getRowDimensions();
668 2
        $newRowDimensions = [];
669
670 2
        foreach ($currentRowDimensions as $objRowDimension) {
671 2
            $newRowDimensions[$objRowDimension->getRowIndex()] = $objRowDimension;
672
        }
673
674 2
        $this->rowDimensions = $newRowDimensions;
675
676 2
        return $this;
677
    }
678
679
    /**
680
     * Calculate worksheet dimension
681
     *
682
     * @return string  String containing the dimension of this worksheet
683
     */
684 58
    public function calculateWorksheetDimension()
685
    {
686
        // Return
687 58
        return 'A1' . ':' . $this->getHighestColumn() . $this->getHighestRow();
688
    }
689
690
    /**
691
     * Calculate worksheet data dimension
692
     *
693
     * @return string  String containing the dimension of this worksheet that actually contain data
694
     */
695
    public function calculateWorksheetDataDimension()
696
    {
697
        // Return
698
        return 'A1' . ':' . $this->getHighestDataColumn() . $this->getHighestDataRow();
699
    }
700
701
    /**
702
     * Calculate widths for auto-size columns
703
     *
704
     * @param  bool  $calculateMergeCells  Calculate merge cell width
705
     * @return Worksheet;
0 ignored issues
show
Documentation introduced by
The doc-type Worksheet; could not be parsed: Expected "|" or "end of type", but got ";" at position 9. (view supported doc-types)

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

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

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
723 8
                    $isMergeCell[$cellReference] = true;
724
                }
725
            }
726
727
            // loop through all cells in the worksheet
728 11
            foreach ($this->getCellCollection(false) as $cellID) {
729 11
                $cell = $this->getCell($cellID, false);
730 11
                if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) {
731
                    // Determine width if cell does not participate in a merge
732 11
                    if (!isset($isMergeCell[$this->cellCollection->getCurrentAddress()])) {
733
                        // Calculated value
734
                        // To formatted string
735 11
                        $cellValue = Style\NumberFormat::toFormattedString(
736 11
                            $cell->getCalculatedValue(),
737 11
                            $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()
738
                        );
739
740 11
                        $autoSizes[$this->cellCollection->getCurrentColumn()] = max(
741 11
                            (float) $autoSizes[$this->cellCollection->getCurrentColumn()],
742 11
                            (float) Shared\Font::calculateColumnWidth(
743 11
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
744
                                $cellValue,
745 11
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(),
746 11
                                $this->getDefaultStyle()->getFont()
0 ignored issues
show
Deprecated Code introduced by
The method PhpOffice\PhpSpreadsheet...heet::getDefaultStyle() has been deprecated.

This method has been deprecated.

Loading history...
747
                            )
748
                        );
749
                    }
750
                }
751
            }
752
753
            // adjust column widths
754 11
            foreach ($autoSizes as $columnIndex => $width) {
755 11
                if ($width == -1) {
756
                    $width = $this->getDefaultColumnDimension()->getWidth();
757
                }
758 11
                $this->getColumnDimension($columnIndex)->setWidth($width);
759
            }
760
        }
761
762 43
        return $this;
763
    }
764
765
    /**
766
     * Get parent
767
     *
768
     * @return Spreadsheet
769
     */
770 70
    public function getParent()
771
    {
772 70
        return $this->parent;
773
    }
774
775
    /**
776
     * Re-bind parent
777
     *
778
     * @param Spreadsheet $parent
779
     * @return Worksheet
780
     */
781 1
    public function rebindParent(Spreadsheet $parent)
782
    {
783 1
        if ($this->parent !== null) {
784 1
            $namedRanges = $this->parent->getNamedRanges();
785 1
            foreach ($namedRanges as $namedRange) {
786
                $parent->addNamedRange($namedRange);
787
            }
788
789 1
            $this->parent->removeSheetByIndex(
790 1
                $this->parent->getIndex($this)
791
            );
792
        }
793 1
        $this->parent = $parent;
794
795 1
        return $this;
796
    }
797
798
    /**
799
     * Get title
800
     *
801
     * @return string
802
     */
803 70
    public function getTitle()
804
    {
805 70
        return $this->title;
806
    }
807
808
    /**
809
     * Set title
810
     *
811
     * @param string $pValue String containing the dimension of this worksheet
812
     * @param string $updateFormulaCellReferences boolean Flag indicating whether cell references in formulae should
813
     *            be updated to reflect the new sheet name.
814
     *          This should be left as the default true, unless you are
815
     *          certain that no formula cells on any worksheet contain
816
     *          references to this worksheet
817
     * @return Worksheet
818
     */
819 70
    public function setTitle($pValue = 'Worksheet', $updateFormulaCellReferences = true)
820
    {
821
        // Is this a 'rename' or not?
822 70
        if ($this->getTitle() == $pValue) {
823 4
            return $this;
824
        }
825
826
        // Syntax check
827 70
        self::checkSheetTitle($pValue);
828
829
        // Old title
830 70
        $oldTitle = $this->getTitle();
831
832 70
        if ($this->parent) {
833
            // Is there already such sheet name?
834 62
            if ($this->parent->sheetNameExists($pValue)) {
835
                // Use name, but append with lowest possible integer
836
837 2
                if (Shared\StringHelper::countCharacters($pValue) > 29) {
838
                    $pValue = Shared\StringHelper::substring($pValue, 0, 29);
839
                }
840 2
                $i = 1;
841 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...
842
                    ++$i;
843
                    if ($i == 10) {
844
                        if (Shared\StringHelper::countCharacters($pValue) > 28) {
845
                            $pValue = Shared\StringHelper::substring($pValue, 0, 28);
846
                        }
847
                    } elseif ($i == 100) {
848
                        if (Shared\StringHelper::countCharacters($pValue) > 27) {
849
                            $pValue = Shared\StringHelper::substring($pValue, 0, 27);
850
                        }
851
                    }
852
                }
853
854 2
                $altTitle = $pValue . ' ' . $i;
855
856 2
                return $this->setTitle($altTitle, $updateFormulaCellReferences);
857
            }
858
        }
859
860
        // Set title
861 70
        $this->title = $pValue;
862 70
        $this->dirty = true;
863
864 70
        if ($this->parent && $this->parent->getCalculationEngine()) {
865
            // New title
866 62
            $newTitle = $this->getTitle();
867 62
            $this->parent->getCalculationEngine()
868 62
                ->renameCalculationCacheForWorksheet($oldTitle, $newTitle);
869 62
            if ($updateFormulaCellReferences) {
870 23
                ReferenceHelper::getInstance()->updateNamedFormulas($this->parent, $oldTitle, $newTitle);
871
            }
872
        }
873
874 70
        return $this;
875
    }
876
877
    /**
878
     * Get sheet state
879
     *
880
     * @return string Sheet state (visible, hidden, veryHidden)
881
     */
882 58
    public function getSheetState()
883
    {
884 58
        return $this->sheetState;
885
    }
886
887
    /**
888
     * Set sheet state
889
     *
890
     * @param string $value Sheet state (visible, hidden, veryHidden)
891
     * @return Worksheet
892
     */
893 70
    public function setSheetState($value = self::SHEETSTATE_VISIBLE)
894
    {
895 70
        $this->sheetState = $value;
896
897 70
        return $this;
898
    }
899
900
    /**
901
     * Get page setup
902
     *
903
     * @return Worksheet\PageSetup
904
     */
905 59
    public function getPageSetup()
906
    {
907 59
        return $this->pageSetup;
908
    }
909
910
    /**
911
     * Set page setup
912
     *
913
     * @param Worksheet\PageSetup    $pValue
914
     * @return Worksheet
915
     */
916
    public function setPageSetup(Worksheet\PageSetup $pValue)
917
    {
918
        $this->pageSetup = $pValue;
919
920
        return $this;
921
    }
922
923
    /**
924
     * Get page margins
925
     *
926
     * @return Worksheet\PageMargins
927
     */
928 59
    public function getPageMargins()
929
    {
930 59
        return $this->pageMargins;
931
    }
932
933
    /**
934
     * Set page margins
935
     *
936
     * @param Worksheet\PageMargins    $pValue
937
     * @return Worksheet
938
     */
939
    public function setPageMargins(Worksheet\PageMargins $pValue)
940
    {
941
        $this->pageMargins = $pValue;
942
943
        return $this;
944
    }
945
946
    /**
947
     * Get page header/footer
948
     *
949
     * @return Worksheet\HeaderFooter
950
     */
951 59
    public function getHeaderFooter()
952
    {
953 59
        return $this->headerFooter;
954
    }
955
956
    /**
957
     * Set page header/footer
958
     *
959
     * @param Worksheet\HeaderFooter    $pValue
960
     * @return Worksheet
961
     */
962
    public function setHeaderFooter(Worksheet\HeaderFooter $pValue)
963
    {
964
        $this->headerFooter = $pValue;
965
966
        return $this;
967
    }
968
969
    /**
970
     * Get sheet view
971
     *
972
     * @return Worksheet\SheetView
973
     */
974 58
    public function getSheetView()
975
    {
976 58
        return $this->sheetView;
977
    }
978
979
    /**
980
     * Set sheet view
981
     *
982
     * @param Worksheet\SheetView    $pValue
983
     * @return Worksheet
984
     */
985
    public function setSheetView(Worksheet\SheetView $pValue)
986
    {
987
        $this->sheetView = $pValue;
988
989
        return $this;
990
    }
991
992
    /**
993
     * Get Protection
994
     *
995
     * @return Worksheet\Protection
996
     */
997 59
    public function getProtection()
998
    {
999 59
        return $this->protection;
1000
    }
1001
1002
    /**
1003
     * Set Protection
1004
     *
1005
     * @param Worksheet\Protection    $pValue
1006
     * @return Worksheet
1007
     */
1008
    public function setProtection(Worksheet\Protection $pValue)
1009
    {
1010
        $this->protection = $pValue;
1011
        $this->dirty = true;
1012
1013
        return $this;
1014
    }
1015
1016
    /**
1017
     * Get highest worksheet column
1018
     *
1019
     * @param   string     $row        Return the data highest column for the specified row,
1020
     *                                     or the highest column of any row if no row number is passed
1021
     * @return string Highest column name
1022
     */
1023 60
    public function getHighestColumn($row = null)
1024
    {
1025 60
        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...
1026 60
            return $this->cachedHighestColumn;
1027
        }
1028
1029
        return $this->getHighestDataColumn($row);
1030
    }
1031
1032
    /**
1033
     * Get highest worksheet column that contains data
1034
     *
1035
     * @param   string     $row        Return the highest data column for the specified row,
1036
     *                                     or the highest data column of any row if no row number is passed
1037
     * @return string Highest column name that contains data
1038
     */
1039 10
    public function getHighestDataColumn($row = null)
1040
    {
1041 10
        return $this->cellCollection->getHighestColumn($row);
1042
    }
1043
1044
    /**
1045
     * Get highest worksheet row
1046
     *
1047
     * @param   string     $column     Return the highest data row for the specified column,
1048
     *                                     or the highest row of any column if no column letter is passed
1049
     * @return int Highest row number
1050
     */
1051 60
    public function getHighestRow($column = null)
1052
    {
1053 60
        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...
1054 60
            return $this->cachedHighestRow;
1055
        }
1056
1057
        return $this->getHighestDataRow($column);
1058
    }
1059
1060
    /**
1061
     * Get highest worksheet row that contains data
1062
     *
1063
     * @param   string     $column     Return the highest data row for the specified column,
1064
     *                                     or the highest data row of any column if no column letter is passed
1065
     * @return string Highest row number that contains data
1066
     */
1067 12
    public function getHighestDataRow($column = null)
1068
    {
1069 12
        return $this->cellCollection->getHighestRow($column);
1070
    }
1071
1072
    /**
1073
     * Get highest worksheet column and highest row that have cell records
1074
     *
1075
     * @return array Highest column name and highest row number
1076
     */
1077
    public function getHighestRowAndColumn()
1078
    {
1079
        return $this->cellCollection->getHighestRowAndColumn();
1080
    }
1081
1082
    /**
1083
     * Set a cell value
1084
     *
1085
     * @param string $pCoordinate Coordinate of the cell
1086
     * @param mixed $pValue Value of the cell
1087
     * @param bool $returnCell   Return the worksheet (false, default) or the cell (true)
1088
     * @return Worksheet|Cell    Depending on the last parameter being specified
1089
     */
1090 36
    public function setCellValue($pCoordinate = 'A1', $pValue = null, $returnCell = false)
1091
    {
1092 36
        $cell = $this->getCell(strtoupper($pCoordinate))->setValue($pValue);
1093
1094 36
        return ($returnCell) ? $cell : $this;
1095
    }
1096
1097
    /**
1098
     * Set a cell value by using numeric cell coordinates
1099
     *
1100
     * @param int $pColumn Numeric column coordinate of the cell (A = 0)
1101
     * @param int $pRow Numeric row coordinate of the cell
1102
     * @param mixed $pValue Value of the cell
1103
     * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
1104
     * @return Worksheet|Cell    Depending on the last parameter being specified
1105
     */
1106
    public function setCellValueByColumnAndRow($pColumn = 0, $pRow = 1, $pValue = null, $returnCell = false)
1107
    {
1108
        $cell = $this->getCellByColumnAndRow($pColumn, $pRow)->setValue($pValue);
1109
1110
        return ($returnCell) ? $cell : $this;
1111
    }
1112
1113
    /**
1114
     * Set a cell value
1115
     *
1116
     * @param string $pCoordinate Coordinate of the cell
1117
     * @param mixed  $pValue Value of the cell
1118
     * @param string $pDataType Explicit data type
1119
     * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
1120
     * @return Worksheet|Cell    Depending on the last parameter being specified
1121
     */
1122
    public function setCellValueExplicit($pCoordinate = 'A1', $pValue = null, $pDataType = Cell\DataType::TYPE_STRING, $returnCell = false)
1123
    {
1124
        // Set value
1125
        $cell = $this->getCell(strtoupper($pCoordinate))->setValueExplicit($pValue, $pDataType);
1126
1127
        return ($returnCell) ? $cell : $this;
1128
    }
1129
1130
    /**
1131
     * Set a cell value by using numeric cell coordinates
1132
     *
1133
     * @param int $pColumn Numeric column coordinate of the cell
1134
     * @param int $pRow Numeric row coordinate of the cell
1135
     * @param mixed $pValue Value of the cell
1136
     * @param string $pDataType Explicit data type
1137
     * @param bool $returnCell Return the worksheet (false, default) or the cell (true)
1138
     * @return Worksheet|Cell    Depending on the last parameter being specified
1139
     */
1140
    public function setCellValueExplicitByColumnAndRow($pColumn = 0, $pRow = 1, $pValue = null, $pDataType = Cell\DataType::TYPE_STRING, $returnCell = false)
1141
    {
1142
        $cell = $this->getCellByColumnAndRow($pColumn, $pRow)->setValueExplicit($pValue, $pDataType);
1143
1144
        return ($returnCell) ? $cell : $this;
1145
    }
1146
1147
    /**
1148
     * Get cell at a specific coordinate
1149
     *
1150
     * @param string $pCoordinate    Coordinate of the cell
1151
     * @param bool $createIfNotExists  Flag indicating whether a new cell should be created if it doesn't
1152
     *                                       already exist, or a null should be returned instead
1153
     * @throws Exception
1154
     * @return null|Cell Cell that was found/created or null
1155
     */
1156 62
    public function getCell($pCoordinate = 'A1', $createIfNotExists = true)
1157
    {
1158
        // Check cell collection
1159 62
        if ($this->cellCollection->isDataSet(strtoupper($pCoordinate))) {
1160 61
            return $this->cellCollection->getCacheData($pCoordinate);
1161
        }
1162
1163
        // Worksheet reference?
1164 61 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...
1165 1
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1166
1167 1
            return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
1168
        }
1169
1170
        // Named range?
1171 61
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1172 61
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1173
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1174
            if ($namedRange !== null) {
1175
                $pCoordinate = $namedRange->getRange();
1176
1177
                return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
1178
            }
1179
        }
1180
1181
        // Uppercase coordinate
1182 61
        $pCoordinate = strtoupper($pCoordinate);
1183
1184 61 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...
1185
            throw new Exception('Cell coordinate can not be a range of cells.');
1186 61
        } elseif (strpos($pCoordinate, '$') !== false) {
1187
            throw new Exception('Cell coordinate must not be absolute.');
1188
        }
1189
1190
        // Create new cell object, if required
1191 61
        return $createIfNotExists ? $this->createNewCell($pCoordinate) : null;
1192
    }
1193
1194
    /**
1195
     * Get cell at a specific coordinate by using numeric cell coordinates
1196
     *
1197
     * @param  string $pColumn Numeric column coordinate of the cell
1198
     * @param string $pRow Numeric row coordinate of the cell
1199
     * @param bool $createIfNotExists  Flag indicating whether a new cell should be created if it doesn't
1200
     *                                       already exist, or a null should be returned instead
1201
     * @return null|Cell Cell that was found/created or null
1202
     */
1203 29
    public function getCellByColumnAndRow($pColumn = 0, $pRow = 1, $createIfNotExists = true)
1204
    {
1205 29
        $columnLetter = Cell::stringFromColumnIndex($pColumn);
1206 29
        $coordinate = $columnLetter . $pRow;
1207
1208 29
        if ($this->cellCollection->isDataSet($coordinate)) {
1209 29
            return $this->cellCollection->getCacheData($coordinate);
1210
        }
1211
1212
        // Create new cell object, if required
1213 17
        return $createIfNotExists ? $this->createNewCell($coordinate) : null;
1214
    }
1215
1216
    /**
1217
     * Create a new cell at the specified coordinate
1218
     *
1219
     * @param string $pCoordinate    Coordinate of the cell
1220
     * @return Cell Cell that was created
1221
     */
1222 62
    private function createNewCell($pCoordinate)
1223
    {
1224 62
        $cell = $this->cellCollection->addCacheData(
1225
            $pCoordinate,
1226 62
            new Cell(null, Cell\DataType::TYPE_NULL, $this)
1227
        );
1228 62
        $this->cellCollectionIsSorted = false;
1229
1230
        // Coordinates
1231 62
        $aCoordinates = Cell::coordinateFromString($pCoordinate);
1232 62
        if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($aCoordinates[0])) {
1233 54
            $this->cachedHighestColumn = $aCoordinates[0];
1234
        }
1235 62
        $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...
1236
1237
        // Cell needs appropriate xfIndex from dimensions records
1238
        //    but don't create dimension records if they don't already exist
1239 62
        $rowDimension = $this->getRowDimension($aCoordinates[1], false);
1240 62
        $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
1241
1242 62
        if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
1243
            // then there is a row dimension with explicit style, assign it to the cell
1244
            $cell->setXfIndex($rowDimension->getXfIndex());
1245 62
        } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
1246
            // then there is a column dimension, assign it to the cell
1247
            $cell->setXfIndex($columnDimension->getXfIndex());
1248
        }
1249
1250 62
        return $cell;
1251
    }
1252
1253
    /**
1254
     * Does the cell at a specific coordinate exist?
1255
     *
1256
     * @param string $pCoordinate  Coordinate of the cell
1257
     * @throws Exception
1258
     * @return bool
1259
     */
1260 37
    public function cellExists($pCoordinate = 'A1')
1261
    {
1262
        // Worksheet reference?
1263 37 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...
1264
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1265
1266
            return $this->parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
1267
        }
1268
1269
        // Named range?
1270 37
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1271 37
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1272
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1273
            if ($namedRange !== null) {
1274
                $pCoordinate = $namedRange->getRange();
1275
                if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
1276
                    if (!$namedRange->getLocalOnly()) {
1277
                        return $namedRange->getWorksheet()->cellExists($pCoordinate);
1278
                    } else {
1279
                        throw new Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
1280
                    }
1281
                }
1282
            } else {
1283
                return false;
1284
            }
1285
        }
1286
1287
        // Uppercase coordinate
1288 37
        $pCoordinate = strtoupper($pCoordinate);
1289
1290 37
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
1291
            throw new Exception('Cell coordinate can not be a range of cells.');
1292 37
        } elseif (strpos($pCoordinate, '$') !== false) {
1293
            throw new Exception('Cell coordinate must not be absolute.');
1294
        } else {
1295
            // Coordinates
1296 37
            $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...
1297
1298
            // Cell exists?
1299 37
            return $this->cellCollection->isDataSet($pCoordinate);
1300
        }
1301
    }
1302
1303
    /**
1304
     * Cell at a specific coordinate by using numeric cell coordinates exists?
1305
     *
1306
     * @param string $pColumn Numeric column coordinate of the cell
1307
     * @param string $pRow Numeric row coordinate of the cell
1308
     * @return bool
1309
     */
1310 3
    public function cellExistsByColumnAndRow($pColumn = 0, $pRow = 1)
1311
    {
1312 3
        return $this->cellExists(Cell::stringFromColumnIndex($pColumn) . $pRow);
1313
    }
1314
1315
    /**
1316
     * Get row dimension at a specific row
1317
     *
1318
     * @param int $pRow Numeric index of the row
1319
     * @return Worksheet\RowDimension
1320
     */
1321 62
    public function getRowDimension($pRow = 1, $create = true)
1322
    {
1323
        // Found
1324 62
        $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...
1325
1326
        // Get row dimension
1327 62
        if (!isset($this->rowDimensions[$pRow])) {
1328 62
            if (!$create) {
1329 61
                return null;
1330
            }
1331 58
            $this->rowDimensions[$pRow] = new Worksheet\RowDimension($pRow);
1332
1333 58
            $this->cachedHighestRow = max($this->cachedHighestRow, $pRow);
1334
        }
1335
1336 58
        return $this->rowDimensions[$pRow];
1337
    }
1338
1339
    /**
1340
     * Get column dimension at a specific column
1341
     *
1342
     * @param string $pColumn String index of the column
1343
     * @return Worksheet\ColumnDimension
1344
     */
1345 62
    public function getColumnDimension($pColumn = 'A', $create = true)
1346
    {
1347
        // Uppercase coordinate
1348 62
        $pColumn = strtoupper($pColumn);
1349
1350
        // Fetch dimensions
1351 62
        if (!isset($this->columnDimensions[$pColumn])) {
1352 62
            if (!$create) {
1353 61
                return null;
1354
            }
1355 26
            $this->columnDimensions[$pColumn] = new Worksheet\ColumnDimension($pColumn);
1356
1357 26
            if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($pColumn)) {
1358 12
                $this->cachedHighestColumn = $pColumn;
1359
            }
1360
        }
1361
1362 26
        return $this->columnDimensions[$pColumn];
1363
    }
1364
1365
    /**
1366
     * Get column dimension at a specific column by using numeric cell coordinates
1367
     *
1368
     * @param int $pColumn Numeric column coordinate of the cell
1369
     * @return Worksheet\ColumnDimension
1370
     */
1371 3
    public function getColumnDimensionByColumn($pColumn = 0)
1372
    {
1373 3
        return $this->getColumnDimension(Cell::stringFromColumnIndex($pColumn));
1374
    }
1375
1376
    /**
1377
     * Get styles
1378
     *
1379
     * @return Style[]
1380
     */
1381
    public function getStyles()
1382
    {
1383
        return $this->styles;
1384
    }
1385
1386
    /**
1387
     * Get default style of workbook.
1388
     *
1389
     * @deprecated
1390
     * @throws Exception
1391
     * @return Style
1392
     */
1393
    public function getDefaultStyle()
1394
    {
1395
        return $this->parent->getDefaultStyle();
1396
    }
1397
1398
    /**
1399
     * Set default style - should only be used by \PhpOffice\PhpSpreadsheet\IReader implementations!
1400
     *
1401
     * @deprecated
1402
     * @param Style $pValue
1403
     * @throws Exception
1404
     * @return Worksheet
1405
     */
1406
    public function setDefaultStyle(Style $pValue)
1407
    {
1408
        $this->parent->getDefaultStyle()->applyFromArray([
1409
            'font' => [
1410
                'name' => $pValue->getFont()->getName(),
1411
                'size' => $pValue->getFont()->getSize(),
1412
            ],
1413
        ]);
1414
1415
        return $this;
1416
    }
1417
1418
    /**
1419
     * Get style for cell
1420
     *
1421
     * @param string $pCellCoordinate Cell coordinate (or range) to get style for
1422
     * @throws Exception
1423
     * @return Style
1424
     */
1425 29
    public function getStyle($pCellCoordinate = 'A1')
1426
    {
1427
        // set this sheet as active
1428 29
        $this->parent->setActiveSheetIndex($this->parent->getIndex($this));
1429
1430
        // set cell coordinate as active
1431 29
        $this->setSelectedCells(strtoupper($pCellCoordinate));
1432
1433 29
        return $this->parent->getCellXfSupervisor();
1434
    }
1435
1436
    /**
1437
     * Get conditional styles for a cell
1438
     *
1439
     * @param string $pCoordinate
1440
     * @return Style\Conditional[]
1441
     */
1442 2
    public function getConditionalStyles($pCoordinate = 'A1')
1443
    {
1444 2
        $pCoordinate = strtoupper($pCoordinate);
1445 2
        if (!isset($this->conditionalStylesCollection[$pCoordinate])) {
1446 2
            $this->conditionalStylesCollection[$pCoordinate] = [];
1447
        }
1448
1449 2
        return $this->conditionalStylesCollection[$pCoordinate];
1450
    }
1451
1452
    /**
1453
     * Do conditional styles exist for this cell?
1454
     *
1455
     * @param string $pCoordinate
1456
     * @return bool
1457
     */
1458 10
    public function conditionalStylesExists($pCoordinate = 'A1')
1459
    {
1460 10
        if (isset($this->conditionalStylesCollection[strtoupper($pCoordinate)])) {
1461
            return true;
1462
        }
1463
1464 10
        return false;
1465
    }
1466
1467
    /**
1468
     * Removes conditional styles for a cell
1469
     *
1470
     * @param string $pCoordinate
1471
     * @return Worksheet
1472
     */
1473 11
    public function removeConditionalStyles($pCoordinate = 'A1')
1474
    {
1475 11
        unset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
1476
1477 11
        return $this;
1478
    }
1479
1480
    /**
1481
     * Get collection of conditional styles
1482
     *
1483
     * @return array
1484
     */
1485 58
    public function getConditionalStylesCollection()
1486
    {
1487 58
        return $this->conditionalStylesCollection;
1488
    }
1489
1490
    /**
1491
     * Set conditional styles
1492
     *
1493
     * @param string $pCoordinate eg: 'A1'
1494
     * @param $pValue Style\Conditional[]
1495
     * @return Worksheet
1496
     */
1497 2
    public function setConditionalStyles($pCoordinate, $pValue)
1498
    {
1499 2
        $this->conditionalStylesCollection[strtoupper($pCoordinate)] = $pValue;
1500
1501 2
        return $this;
1502
    }
1503
1504
    /**
1505
     * Get style for cell by using numeric cell coordinates
1506
     *
1507
     * @param int $pColumn  Numeric column coordinate of the cell
1508
     * @param int $pRow Numeric row coordinate of the cell
1509
     * @param int pColumn2 Numeric column coordinate of the range cell
1510
     * @param int pRow2 Numeric row coordinate of the range cell
1511
     * @return Style
1512
     */
1513
    public function getStyleByColumnAndRow($pColumn = 0, $pRow = 1, $pColumn2 = null, $pRow2 = null)
1514
    {
1515
        if (!is_null($pColumn2) && !is_null($pRow2)) {
1516
            $cellRange = Cell::stringFromColumnIndex($pColumn) . $pRow . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1517
1518
            return $this->getStyle($cellRange);
1519
        }
1520
1521
        return $this->getStyle(Cell::stringFromColumnIndex($pColumn) . $pRow);
1522
    }
1523
1524
    /**
1525
     * Set shared cell style to a range of cells
1526
     *
1527
     * Please note that this will overwrite existing cell styles for cells in range!
1528
     *
1529
     * @deprecated duplicateStyle
1530
     * @param Style $pSharedCellStyle Cell style to share
1531
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1532
     * @throws Exception
1533
     * @return Worksheet
1534
     */
1535
    public function setSharedStyle(Style $pSharedCellStyle = null, $pRange = '')
1536
    {
1537
        $this->duplicateStyle($pSharedCellStyle, $pRange);
1538
1539
        return $this;
1540
    }
1541
1542
    /**
1543
     * Duplicate cell style to a range of cells
1544
     *
1545
     * Please note that this will overwrite existing cell styles for cells in range!
1546
     *
1547
     * @param Style $pCellStyle Cell style to duplicate
1548
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1549
     * @throws Exception
1550
     * @return Worksheet
1551
     */
1552 2
    public function duplicateStyle(Style $pCellStyle = null, $pRange = '')
1553
    {
1554
        // make sure we have a real style and not supervisor
1555 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...
1556
1557
        // Add the style to the workbook if necessary
1558 2
        $workbook = $this->parent;
1559 2
        if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
1560
            // there is already such cell Xf in our collection
1561 1
            $xfIndex = $existingStyle->getIndex();
1562
        } else {
1563
            // we don't have such a cell Xf, need to add
1564 2
            $workbook->addCellXf($pCellStyle);
0 ignored issues
show
Bug introduced by
It seems like $pCellStyle defined by parameter $pCellStyle on line 1552 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...
1565 2
            $xfIndex = $pCellStyle->getIndex();
1566
        }
1567
1568
        // Calculate range outer borders
1569 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1570
1571
        // Make sure we can loop upwards on rows and columns
1572 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...
1573
            $tmp = $rangeStart;
1574
            $rangeStart = $rangeEnd;
1575
            $rangeEnd = $tmp;
1576
        }
1577
1578
        // Loop through cells and apply styles
1579 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...
1580 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1581 2
                $this->getCell(Cell::stringFromColumnIndex($col - 1) . $row)->setXfIndex($xfIndex);
1582
            }
1583
        }
1584
1585 2
        return $this;
1586
    }
1587
1588
    /**
1589
     * Duplicate conditional style to a range of cells
1590
     *
1591
     * Please note that this will overwrite existing cell styles for cells in range!
1592
     *
1593
     * @param    Style\Conditional[]    $pCellStyle    Cell style to duplicate
1594
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1595
     * @throws Exception
1596
     * @return Worksheet
1597
     */
1598 2
    public function duplicateConditionalStyle(array $pCellStyle = null, $pRange = '')
1599
    {
1600 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...
1601 2
            if (!($cellStyle instanceof Style\Conditional)) {
1602 2
                throw new Exception('Style is not a conditional style');
1603
            }
1604
        }
1605
1606
        // Calculate range outer borders
1607 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1608
1609
        // Make sure we can loop upwards on rows and columns
1610 2 View Code Duplication
        if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

Loading history...
1618 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1619 2
                $this->setConditionalStyles(Cell::stringFromColumnIndex($col - 1) . $row, $pCellStyle);
1620
            }
1621
        }
1622
1623 2
        return $this;
1624
    }
1625
1626
    /**
1627
     * Duplicate cell style array to a range of cells
1628
     *
1629
     * Please note that this will overwrite existing cell styles for cells in range,
1630
     * if they are in the styles array. For example, if you decide to set a range of
1631
     * cells to font bold, only include font bold in the styles array.
1632
     *
1633
     * @deprecated
1634
     * @param array $pStyles Array containing style information
1635
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1636
     * @param bool $pAdvanced Advanced mode for setting borders.
1637
     * @throws Exception
1638
     * @return Worksheet
1639
     */
1640
    public function duplicateStyleArray($pStyles = null, $pRange = '', $pAdvanced = true)
1641
    {
1642
        $this->getStyle($pRange)->applyFromArray($pStyles, $pAdvanced);
1643
1644
        return $this;
1645
    }
1646
1647
    /**
1648
     * Set break on a cell
1649
     *
1650
     * @param string $pCell Cell coordinate (e.g. A1)
1651
     * @param int $pBreak Break type (type of Worksheet::BREAK_*)
1652
     * @throws Exception
1653
     * @return Worksheet
1654
     */
1655 1
    public function setBreak($pCell = 'A1', $pBreak = self::BREAK_NONE)
1656
    {
1657
        // Uppercase coordinate
1658 1
        $pCell = strtoupper($pCell);
1659
1660 1
        if ($pCell != '') {
1661 1
            if ($pBreak == self::BREAK_NONE) {
1662
                if (isset($this->breaks[$pCell])) {
1663
                    unset($this->breaks[$pCell]);
1664
                }
1665
            } else {
1666 1
                $this->breaks[$pCell] = $pBreak;
1667
            }
1668
        } else {
1669
            throw new Exception('No cell coordinate specified.');
1670
        }
1671
1672 1
        return $this;
1673
    }
1674
1675
    /**
1676
     * Set break on a cell by using numeric cell coordinates
1677
     *
1678
     * @param int $pColumn Numeric column coordinate of the cell
1679
     * @param int $pRow Numeric row coordinate of the cell
1680
     * @param  int $pBreak Break type (type of \PhpOffice\PhpSpreadsheet\Worksheet::BREAK_*)
1681
     * @return Worksheet
1682
     */
1683
    public function setBreakByColumnAndRow($pColumn = 0, $pRow = 1, $pBreak = self::BREAK_NONE)
1684
    {
1685
        return $this->setBreak(Cell::stringFromColumnIndex($pColumn) . $pRow, $pBreak);
1686
    }
1687
1688
    /**
1689
     * Get breaks
1690
     *
1691
     * @return array[]
1692
     */
1693 59
    public function getBreaks()
1694
    {
1695 59
        return $this->breaks;
1696
    }
1697
1698
    /**
1699
     * Set merge on a cell range
1700
     *
1701
     * @param string $pRange  Cell range (e.g. A1:E1)
1702
     * @throws Exception
1703
     * @return Worksheet
1704
     */
1705 15
    public function mergeCells($pRange = 'A1:A1')
1706
    {
1707
        // Uppercase coordinate
1708 15
        $pRange = strtoupper($pRange);
1709
1710 15
        if (strpos($pRange, ':') !== false) {
1711 15
            $this->mergeCells[$pRange] = $pRange;
1712
1713
            // make sure cells are created
1714
1715
            // get the cells in the range
1716 15
            $aReferences = Cell::extractAllCellReferencesInRange($pRange);
1717
1718
            // create upper left cell if it does not already exist
1719 15
            $upperLeft = $aReferences[0];
1720 15
            if (!$this->cellExists($upperLeft)) {
1721 10
                $this->getCell($upperLeft)->setValueExplicit(null, Cell\DataType::TYPE_NULL);
1722
            }
1723
1724
            // Blank out the rest of the cells in the range (if they exist)
1725 15
            $count = count($aReferences);
1726 15
            for ($i = 1; $i < $count; ++$i) {
1727 15
                if ($this->cellExists($aReferences[$i])) {
1728 4
                    $this->getCell($aReferences[$i])->setValueExplicit(null, Cell\DataType::TYPE_NULL);
1729
                }
1730
            }
1731
        } else {
1732
            throw new Exception('Merge must be set on a range of cells.');
1733
        }
1734
1735 15
        return $this;
1736
    }
1737
1738
    /**
1739
     * Set merge on a cell range by using numeric cell coordinates
1740
     *
1741
     * @param int $pColumn1    Numeric column coordinate of the first cell
1742
     * @param int $pRow1        Numeric row coordinate of the first cell
1743
     * @param int $pColumn2    Numeric column coordinate of the last cell
1744
     * @param int $pRow2        Numeric row coordinate of the last cell
1745
     * @throws    Exception
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
     * @throws    Exception
1760
     * @return Worksheet
1761
     */
1762 9
    public function unmergeCells($pRange = 'A1:A1')
1763
    {
1764
        // Uppercase coordinate
1765 9
        $pRange = strtoupper($pRange);
1766
1767 9
        if (strpos($pRange, ':') !== false) {
1768 9
            if (isset($this->mergeCells[$pRange])) {
1769 9
                unset($this->mergeCells[$pRange]);
1770
            } else {
1771 9
                throw new Exception('Cell range ' . $pRange . ' not known as merged.');
1772
            }
1773
        } else {
1774
            throw new Exception('Merge can only be removed from a range of cells.');
1775
        }
1776
1777 9
        return $this;
1778
    }
1779
1780
    /**
1781
     * Remove merge on a cell range by using numeric cell coordinates
1782
     *
1783
     * @param int $pColumn1    Numeric column coordinate of the first cell
1784
     * @param int $pRow1        Numeric row coordinate of the first cell
1785
     * @param int $pColumn2    Numeric column coordinate of the last cell
1786
     * @param int $pRow2        Numeric row coordinate of the last cell
1787
     * @throws    Exception
1788
     * @return Worksheet
1789
     */
1790
    public function unmergeCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
1791
    {
1792
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1793
1794
        return $this->unmergeCells($cellRange);
1795
    }
1796
1797
    /**
1798
     * Get merge cells array.
1799
     *
1800
     * @return array[]
1801
     */
1802 59
    public function getMergeCells()
1803
    {
1804 59
        return $this->mergeCells;
1805
    }
1806
1807
    /**
1808
     * Set merge cells array for the entire sheet. Use instead mergeCells() to merge
1809
     * a single cell range.
1810
     *
1811
     * @param array
1812
     */
1813 12
    public function setMergeCells($pValue = [])
1814
    {
1815 12
        $this->mergeCells = $pValue;
1816
1817 12
        return $this;
1818
    }
1819
1820
    /**
1821
     * Set protection on a cell range
1822
     *
1823
     * @param    string            $pRange                Cell (e.g. A1) or cell range (e.g. A1:E1)
1824
     * @param    string            $pPassword            Password to unlock the protection
1825
     * @param    bool        $pAlreadyHashed    If the password has already been hashed, set this to true
1826
     * @throws    Exception
1827
     * @return Worksheet
1828
     */
1829 9
    public function protectCells($pRange = 'A1', $pPassword = '', $pAlreadyHashed = false)
1830
    {
1831
        // Uppercase coordinate
1832 9
        $pRange = strtoupper($pRange);
1833
1834 9
        if (!$pAlreadyHashed) {
1835 9
            $pPassword = Shared\PasswordHasher::hashPassword($pPassword);
1836
        }
1837 9
        $this->protectedCells[$pRange] = $pPassword;
1838
1839 9
        return $this;
1840
    }
1841
1842
    /**
1843
     * Set protection on a cell range by using numeric cell coordinates
1844
     *
1845
     * @param int  $pColumn1            Numeric column coordinate of the first cell
1846
     * @param int  $pRow1                Numeric row coordinate of the first cell
1847
     * @param int  $pColumn2            Numeric column coordinate of the last cell
1848
     * @param int  $pRow2                Numeric row coordinate of the last cell
1849
     * @param string $pPassword            Password to unlock the protection
1850
     * @param    bool $pAlreadyHashed    If the password has already been hashed, set this to true
1851
     * @throws    Exception
1852
     * @return Worksheet
1853
     */
1854
    public function protectCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1, $pPassword = '', $pAlreadyHashed = false)
1855
    {
1856
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1857
1858
        return $this->protectCells($cellRange, $pPassword, $pAlreadyHashed);
1859
    }
1860
1861
    /**
1862
     * Remove protection on a cell range
1863
     *
1864
     * @param    string            $pRange        Cell (e.g. A1) or cell range (e.g. A1:E1)
1865
     * @throws    Exception
1866
     * @return Worksheet
1867
     */
1868 9
    public function unprotectCells($pRange = 'A1')
1869
    {
1870
        // Uppercase coordinate
1871 9
        $pRange = strtoupper($pRange);
1872
1873 9
        if (isset($this->protectedCells[$pRange])) {
1874 9
            unset($this->protectedCells[$pRange]);
1875
        } else {
1876
            throw new Exception('Cell range ' . $pRange . ' not known as protected.');
1877
        }
1878
1879 9
        return $this;
1880
    }
1881
1882
    /**
1883
     * Remove protection on a cell range by using numeric cell coordinates
1884
     *
1885
     * @param int  $pColumn1            Numeric column coordinate of the first cell
1886
     * @param int  $pRow1                Numeric row coordinate of the first cell
1887
     * @param int  $pColumn2            Numeric column coordinate of the last cell
1888
     * @param int $pRow2                Numeric row coordinate of the last cell
1889
     * @param string $pPassword            Password to unlock the protection
1890
     * @param    bool $pAlreadyHashed    If the password has already been hashed, set this to true
1891
     * @throws    Exception
1892
     * @return Worksheet
1893
     */
1894
    public function unprotectCellsByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1, $pPassword = '', $pAlreadyHashed = false)
1895
    {
1896
        $cellRange = Cell::stringFromColumnIndex($pColumn1) . $pRow1 . ':' . Cell::stringFromColumnIndex($pColumn2) . $pRow2;
1897
1898
        return $this->unprotectCells($cellRange, $pPassword, $pAlreadyHashed);
0 ignored issues
show
Unused Code introduced by
The call to Worksheet::unprotectCells() has too many arguments starting with $pPassword.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

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

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

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

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

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

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