Completed
Push — develop ( 2922a1...cfa1fe )
by Adrien
24:40
created

Worksheet::getChartByName()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 4
nop 1
dl 0
loc 14
rs 9.2
c 0
b 0
f 0
ccs 0
cts 7
cp 0
crap 20
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
    public function __construct(Spreadsheet $parent = null, $pTitle = 'Worksheet')
333
    {
334 9
        // Set parent and title
335
        $this->parent = $parent;
336
        $this->setTitle($pTitle, false);
337 9
        // setTitle can change $pTitle
338 9
        $this->setCodeName($this->getTitle());
339
        $this->setSheetState(self::SHEETSTATE_VISIBLE);
340 9
341 9
        $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 9
        $this->pageSetup = new Worksheet\PageSetup();
344
        // Set page margins
345 9
        $this->pageMargins = new Worksheet\PageMargins();
346
        // Set page header/footer
347 9
        $this->headerFooter = new Worksheet\HeaderFooter();
348
        // Set sheet view
349 9
        $this->sheetView = new Worksheet\SheetView();
350
        // Drawing collection
351 9
        $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 9
        $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 9
        $this->protection = new Worksheet\Protection();
356
        // Default row dimension
357 9
        $this->defaultRowDimension = new Worksheet\RowDimension(null);
358
        // Default column dimension
359 9
        $this->defaultColumnDimension = new Worksheet\ColumnDimension(null);
360
        $this->autoFilter = new Worksheet\AutoFilter(null, $this);
361 9
    }
362 9
363 9
    /**
364
     * Disconnect all cells from this Worksheet object,
365
     *    typically so that the worksheet object can be unset
366
     */
367
    public function disconnectCells()
368
    {
369
        if ($this->cellCollection !== null) {
370
            $this->cellCollection->unsetWorksheetCells();
371
            $this->cellCollection = null;
372
        }
373
        //    detach ourself from the workbook, so that it can then delete this worksheet successfully
374
        $this->parent = null;
375
    }
376
377
    /**
378
     * Code to execute when this worksheet is unset()
379
     */
380
    public function __destruct()
381
    {
382
        Calculation::getInstance($this->parent)->clearCalculationCacheForWorksheet($this->title);
383
384
        $this->disconnectCells();
385
    }
386
387
    /**
388
     * Return the cache controller for the cell collection
389
     *
390
     * @return CachedObjectStorage_xxx
391
     */
392
    public function getCellCacheController()
393
    {
394 1
        return $this->cellCollection;
395
    }
396 1
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
    private static function checkSheetCodeName($pValue)
415
    {
416 9
        $CharCount = Shared\StringHelper::countCharacters($pValue);
417
        if ($CharCount == 0) {
418 9
            throw new Exception('Sheet code name cannot be empty.');
419 9
        }
420
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ] and  first and last characters cannot be a "'"
421
        if ((str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) ||
422
            (Shared\StringHelper::substring($pValue, -1, 1) == '\'') ||
423 9
            (Shared\StringHelper::substring($pValue, 0, 1) == '\'')) {
424 9
            throw new Exception('Invalid character found in sheet code name');
425 9
        }
426
427
        // Maximum 31 characters allowed for sheet title
428
        if ($CharCount > 31) {
429
            throw new Exception('Maximum 31 characters allowed in sheet code name.');
430 9
        }
431
432
        return $pValue;
433
    }
434 9
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
    private static function checkSheetTitle($pValue)
443
    {
444 9
        // Some of the printable ASCII characters are invalid:  * : / \ ? [ ]
445
        if (str_replace(self::$invalidCharacters, '', $pValue) !== $pValue) {
446
            throw new Exception('Invalid character found in sheet title');
447 9
        }
448
449
        // Maximum 31 characters allowed for sheet title
450
        if (Shared\StringHelper::countCharacters($pValue) > 31) {
451
            throw new Exception('Maximum 31 characters allowed in sheet title.');
452 9
        }
453
454
        return $pValue;
455
    }
456 9
457
    /**
458
     * Get collection of cells
459
     *
460
     * @param bool $pSorted Also sort the cell collection?
461
     * @return Cell[]
462
     */
463
    public function getCellCollection($pSorted = true)
464
    {
465 1
        if ($pSorted) {
466
            // Re-order cell collection
467 1
            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 1
        if ($this->cellCollection !== null) {
470
            return $this->cellCollection->getCellList();
471
        }
472
473
        return [];
474
    }
475
476
    /**
477
     * Sort collection of cells
478
     *
479
     * @return Worksheet
480
     */
481
    public function sortCellCollection()
482
    {
483 1
        if ($this->cellCollection !== null) {
484
            return $this->cellCollection->getSortedCellList();
485 1
        }
486 1
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
    public function getRowDimensions()
496
    {
497
        return $this->rowDimensions;
498
    }
499
500
    /**
501
     * Get default row dimension
502
     *
503
     * @return Worksheet\RowDimension
504
     */
505
    public function getDefaultRowDimension()
506
    {
507
        return $this->defaultRowDimension;
508
    }
509
510
    /**
511
     * Get collection of column dimensions
512
     *
513
     * @return Worksheet\ColumnDimension[]
514
     */
515
    public function getColumnDimensions()
516
    {
517
        return $this->columnDimensions;
518
    }
519
520
    /**
521
     * Get default column dimension
522
     *
523
     * @return Worksheet\ColumnDimension
524
     */
525
    public function getDefaultColumnDimension()
526
    {
527
        return $this->defaultColumnDimension;
528
    }
529
530
    /**
531
     * Get collection of drawings
532
     *
533
     * @return Worksheet\BaseDrawing[]
534
     */
535
    public function getDrawingCollection()
536
    {
537
        return $this->drawingCollection;
538
    }
539
540
    /**
541
     * Get collection of charts
542
     *
543
     * @return Chart[]
544
     */
545
    public function getChartCollection()
546
    {
547
        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
    public function addChart(Chart $pChart = null, $iChartIndex = null)
558
    {
559
        $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
        if (is_null($iChartIndex)) {
561
            $this->chartCollection[] = $pChart;
562
        } else {
563
            // Insert the chart at the requested index
564
            array_splice($this->chartCollection, $iChartIndex, 0, [$pChart]);
565
        }
566
567
        return $pChart;
568
    }
569
570
    /**
571
     * Return the count of charts on this worksheet
572
     *
573
     * @return int        The number of charts
574
     */
575
    public function getChartCount()
576
    {
577
        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
    public function getChartByIndex($index = null)
588
    {
589
        $chartCount = count($this->chartCollection);
590
        if ($chartCount == 0) {
591
            return false;
592
        }
593
        if (is_null($index)) {
594
            $index = --$chartCount;
595
        }
596
        if (!isset($this->chartCollection[$index])) {
597
            return false;
598
        }
599
600
        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
    public function getChartNames()
610
    {
611
        $chartNames = [];
612
        foreach ($this->chartCollection as $chart) {
613
            $chartNames[] = $chart->getName();
614
        }
615
616
        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
    public function getChartByName($chartName = '')
627
    {
628
        $chartCount = count($this->chartCollection);
629
        if ($chartCount == 0) {
630
            return false;
631
        }
632
        foreach ($this->chartCollection as $index => $chart) {
633
            if ($chart->getName() == $chartName) {
634
                return $this->chartCollection[$index];
635
            }
636
        }
637
638
        return false;
639
    }
640
641
    /**
642
     * Refresh column dimensions
643
     *
644
     * @return Worksheet
645
     */
646
    public function refreshColumnDimensions()
647
    {
648
        $currentColumnDimensions = $this->getColumnDimensions();
649
        $newColumnDimensions = [];
650
651
        foreach ($currentColumnDimensions as $objColumnDimension) {
652
            $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
653
        }
654
655
        $this->columnDimensions = $newColumnDimensions;
656
657
        return $this;
658
    }
659
660
    /**
661
     * Refresh row dimensions
662
     *
663
     * @return Worksheet
664
     */
665
    public function refreshRowDimensions()
666
    {
667
        $currentRowDimensions = $this->getRowDimensions();
668
        $newRowDimensions = [];
669
670
        foreach ($currentRowDimensions as $objRowDimension) {
671
            $newRowDimensions[$objRowDimension->getRowIndex()] = $objRowDimension;
672
        }
673
674
        $this->rowDimensions = $newRowDimensions;
675
676
        return $this;
677
    }
678
679
    /**
680
     * Calculate worksheet dimension
681
     *
682
     * @return string  String containing the dimension of this worksheet
683
     */
684
    public function calculateWorksheetDimension()
685
    {
686
        // Return
687
        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
    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
        $autoSizes = [];
711
        foreach ($this->getColumnDimensions() as $colDimension) {
712
            if ($colDimension->getAutoSize()) {
713
                $autoSizes[$colDimension->getColumnIndex()] = -1;
714
            }
715
        }
716
717
        // There is only something to do if there are some auto-size columns
718
        if (!empty($autoSizes)) {
719
            // build list of cells references that participate in a merge
720
            $isMergeCell = [];
721
            foreach ($this->getMergeCells() as $cells) {
722
                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
                    $isMergeCell[$cellReference] = true;
724
                }
725
            }
726
727
            // loop through all cells in the worksheet
728
            foreach ($this->getCellCollection(false) as $cellID) {
729
                $cell = $this->getCell($cellID, false);
730
                if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) {
731
                    // Determine width if cell does not participate in a merge
732
                    if (!isset($isMergeCell[$this->cellCollection->getCurrentAddress()])) {
733
                        // Calculated value
734
                        // To formatted string
735
                        $cellValue = Style\NumberFormat::toFormattedString(
736
                            $cell->getCalculatedValue(),
737
                            $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()
738
                        );
739
740
                        $autoSizes[$this->cellCollection->getCurrentColumn()] = max(
741
                            (float) $autoSizes[$this->cellCollection->getCurrentColumn()],
742
                            (float) Shared\Font::calculateColumnWidth(
743
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
744
                                $cellValue,
745
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(),
746
                                $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
            foreach ($autoSizes as $columnIndex => $width) {
755
                if ($width == -1) {
756
                    $width = $this->getDefaultColumnDimension()->getWidth();
757
                }
758
                $this->getColumnDimension($columnIndex)->setWidth($width);
759
            }
760
        }
761
762
        return $this;
763
    }
764
765
    /**
766
     * Get parent
767
     *
768
     * @return Spreadsheet
769
     */
770
    public function getParent()
771
    {
772 9
        return $this->parent;
773
    }
774 9
775
    /**
776
     * Re-bind parent
777
     *
778
     * @param Spreadsheet $parent
779
     * @return Worksheet
780
     */
781
    public function rebindParent(Spreadsheet $parent)
782
    {
783
        if ($this->parent !== null) {
784
            $namedRanges = $this->parent->getNamedRanges();
785
            foreach ($namedRanges as $namedRange) {
786
                $parent->addNamedRange($namedRange);
787
            }
788
789
            $this->parent->removeSheetByIndex(
790
                $this->parent->getIndex($this)
791
            );
792
        }
793
        $this->parent = $parent;
794
795
        return $this;
796
    }
797
798
    /**
799
     * Get title
800
     *
801
     * @return string
802
     */
803
    public function getTitle()
804
    {
805 9
        return $this->title;
806
    }
807 9
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
    public function setTitle($pValue = 'Worksheet', $updateFormulaCellReferences = true)
820
    {
821 9
        // Is this a 'rename' or not?
822
        if ($this->getTitle() == $pValue) {
823
            return $this;
824 9
        }
825
826
        // Syntax check
827
        self::checkSheetTitle($pValue);
828
829 9
        // Old title
830
        $oldTitle = $this->getTitle();
831
832 9
        if ($this->parent) {
833
            // Is there already such sheet name?
834 9
            if ($this->parent->sheetNameExists($pValue)) {
835
                // Use name, but append with lowest possible integer
836 1
837
                if (Shared\StringHelper::countCharacters($pValue) > 29) {
838
                    $pValue = Shared\StringHelper::substring($pValue, 0, 29);
839
                }
840
                $i = 1;
841 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
                $altTitle = $pValue . ' ' . $i;
855
856
                return $this->setTitle($altTitle, $updateFormulaCellReferences);
857
            }
858
        }
859
860
        // Set title
861
        $this->title = $pValue;
862
        $this->dirty = true;
863 9
864 9
        if ($this->parent && $this->parent->getCalculationEngine()) {
865
            // New title
866 9
            $newTitle = $this->getTitle();
867
            $this->parent->getCalculationEngine()
868 1
                ->renameCalculationCacheForWorksheet($oldTitle, $newTitle);
869 1
            if ($updateFormulaCellReferences) {
870 1
                ReferenceHelper::getInstance()->updateNamedFormulas($this->parent, $oldTitle, $newTitle);
871 1
            }
872
        }
873
874
        return $this;
875
    }
876 9
877
    /**
878
     * Get sheet state
879
     *
880
     * @return string Sheet state (visible, hidden, veryHidden)
881
     */
882
    public function getSheetState()
883
    {
884
        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
    public function setSheetState($value = self::SHEETSTATE_VISIBLE)
894
    {
895 9
        $this->sheetState = $value;
896
897 9
        return $this;
898
    }
899 9
900
    /**
901
     * Get page setup
902
     *
903
     * @return Worksheet\PageSetup
904
     */
905
    public function getPageSetup()
906
    {
907
        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
    public function getPageMargins()
929
    {
930
        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
    public function getHeaderFooter()
952
    {
953
        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
    public function getSheetView()
975
    {
976
        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
    public function getProtection()
998
    {
999
        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
    public function getHighestColumn($row = null)
1024
    {
1025
        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
            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
    public function getHighestDataColumn($row = null)
1040
    {
1041
        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
    public function getHighestRow($column = null)
1052
    {
1053
        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
            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
    public function getHighestDataRow($column = null)
1068
    {
1069
        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
    public function setCellValue($pCoordinate = 'A1', $pValue = null, $returnCell = false)
1091
    {
1092 1
        $cell = $this->getCell(strtoupper($pCoordinate))->setValue($pValue);
1093
1094 1
        return ($returnCell) ? $cell : $this;
1095
    }
1096 1
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
    public function getCell($pCoordinate = 'A1', $createIfNotExists = true)
1157
    {
1158 1
        // Check cell collection
1159
        if ($this->cellCollection->isDataSet(strtoupper($pCoordinate))) {
1160
            return $this->cellCollection->getCacheData($pCoordinate);
1161 1
        }
1162
1163
        // Worksheet reference?
1164 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
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1166 1
1167
            return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
1168
        }
1169
1170
        // Named range?
1171
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1172
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1173 1
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1174 1
            if ($namedRange !== null) {
1175
                $pCoordinate = $namedRange->getRange();
1176
1177
                return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
1178
            }
1179
        }
1180
1181
        // Uppercase coordinate
1182
        $pCoordinate = strtoupper($pCoordinate);
1183
1184 1 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 1
        } elseif (strpos($pCoordinate, '$') !== false) {
1187
            throw new Exception('Cell coordinate must not be absolute.');
1188 1
        }
1189
1190
        // Create new cell object, if required
1191
        return $createIfNotExists ? $this->createNewCell($pCoordinate) : null;
1192
    }
1193 1
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
    public function getCellByColumnAndRow($pColumn = 0, $pRow = 1, $createIfNotExists = true)
1204
    {
1205
        $columnLetter = Cell::stringFromColumnIndex($pColumn);
1206
        $coordinate = $columnLetter . $pRow;
1207
1208
        if ($this->cellCollection->isDataSet($coordinate)) {
1209
            return $this->cellCollection->getCacheData($coordinate);
1210
        }
1211
1212
        // Create new cell object, if required
1213
        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
    private function createNewCell($pCoordinate)
1223
    {
1224 1
        $cell = $this->cellCollection->addCacheData(
1225
            $pCoordinate,
1226 1
            new Cell(null, Cell\DataType::TYPE_NULL, $this)
1227
        );
1228 1
        $this->cellCollectionIsSorted = false;
1229
1230 1
        // Coordinates
1231
        $aCoordinates = Cell::coordinateFromString($pCoordinate);
1232
        if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($aCoordinates[0])) {
1233 1
            $this->cachedHighestColumn = $aCoordinates[0];
1234 1
        }
1235
        $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 1
        // Cell needs appropriate xfIndex from dimensions records
1238
        //    but don't create dimension records if they don't already exist
1239
        $rowDimension = $this->getRowDimension($aCoordinates[1], false);
1240
        $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
1241 1
1242 1
        if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
1243
            // then there is a row dimension with explicit style, assign it to the cell
1244 1
            $cell->setXfIndex($rowDimension->getXfIndex());
1245
        } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
1246
            // then there is a column dimension, assign it to the cell
1247 1
            $cell->setXfIndex($columnDimension->getXfIndex());
1248
        }
1249
1250
        return $cell;
1251
    }
1252 1
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
    public function cellExists($pCoordinate = 'A1')
1261
    {
1262
        // Worksheet reference?
1263 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
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1271
            (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
        $pCoordinate = strtoupper($pCoordinate);
1289
1290
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
1291
            throw new Exception('Cell coordinate can not be a range of cells.');
1292
        } elseif (strpos($pCoordinate, '$') !== false) {
1293
            throw new Exception('Cell coordinate must not be absolute.');
1294
        } else {
1295
            // Coordinates
1296
            $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
            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
    public function cellExistsByColumnAndRow($pColumn = 0, $pRow = 1)
1311
    {
1312
        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
    public function getRowDimension($pRow = 1, $create = true)
1322
    {
1323 1
        // Found
1324
        $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 1
        // Get row dimension
1327
        if (!isset($this->rowDimensions[$pRow])) {
1328
            if (!$create) {
1329 1
                return null;
1330 1
            }
1331 1
            $this->rowDimensions[$pRow] = new Worksheet\RowDimension($pRow);
1332
1333
            $this->cachedHighestRow = max($this->cachedHighestRow, $pRow);
1334
        }
1335
1336
        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
    public function getColumnDimension($pColumn = 'A', $create = true)
1346
    {
1347 1
        // Uppercase coordinate
1348
        $pColumn = strtoupper($pColumn);
1349
1350 1
        // Fetch dimensions
1351
        if (!isset($this->columnDimensions[$pColumn])) {
1352
            if (!$create) {
1353 1
                return null;
1354 1
            }
1355 1
            $this->columnDimensions[$pColumn] = new Worksheet\ColumnDimension($pColumn);
1356
1357
            if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($pColumn)) {
1358
                $this->cachedHighestColumn = $pColumn;
1359
            }
1360
        }
1361
1362
        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
    public function getColumnDimensionByColumn($pColumn = 0)
1372
    {
1373
        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
    public function getStyle($pCellCoordinate = 'A1')
1426
    {
1427
        // set this sheet as active
1428
        $this->parent->setActiveSheetIndex($this->parent->getIndex($this));
1429
1430
        // set cell coordinate as active
1431
        $this->setSelectedCells(strtoupper($pCellCoordinate));
1432
1433
        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
    public function getConditionalStyles($pCoordinate = 'A1')
1443
    {
1444
        $pCoordinate = strtoupper($pCoordinate);
1445
        if (!isset($this->conditionalStylesCollection[$pCoordinate])) {
1446
            $this->conditionalStylesCollection[$pCoordinate] = [];
1447
        }
1448
1449
        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
    public function conditionalStylesExists($pCoordinate = 'A1')
1459
    {
1460
        if (isset($this->conditionalStylesCollection[strtoupper($pCoordinate)])) {
1461
            return true;
1462
        }
1463
1464
        return false;
1465
    }
1466
1467
    /**
1468
     * Removes conditional styles for a cell
1469
     *
1470
     * @param string $pCoordinate
1471
     * @return Worksheet
1472
     */
1473
    public function removeConditionalStyles($pCoordinate = 'A1')
1474
    {
1475
        unset($this->conditionalStylesCollection[strtoupper($pCoordinate)]);
1476
1477
        return $this;
1478
    }
1479
1480
    /**
1481
     * Get collection of conditional styles
1482
     *
1483
     * @return array
1484
     */
1485
    public function getConditionalStylesCollection()
1486
    {
1487
        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
    public function setConditionalStyles($pCoordinate, $pValue)
1498
    {
1499
        $this->conditionalStylesCollection[strtoupper($pCoordinate)] = $pValue;
1500
1501
        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
    public function duplicateStyle(Style $pCellStyle = null, $pRange = '')
1553
    {
1554
        // make sure we have a real style and not supervisor
1555
        $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
        $workbook = $this->parent;
1559
        if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
1560
            // there is already such cell Xf in our collection
1561
            $xfIndex = $existingStyle->getIndex();
1562
        } else {
1563
            // we don't have such a cell Xf, need to add
1564
            $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
            $xfIndex = $pCellStyle->getIndex();
1566
        }
1567
1568
        // Calculate range outer borders
1569
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1570
1571
        // Make sure we can loop upwards on rows and columns
1572 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 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
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1581
                $this->getCell(Cell::stringFromColumnIndex($col - 1) . $row)->setXfIndex($xfIndex);
1582
            }
1583
        }
1584
1585
        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
    public function duplicateConditionalStyle(array $pCellStyle = null, $pRange = '')
1599
    {
1600
        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
            if (!($cellStyle instanceof Style\Conditional)) {
1602
                throw new Exception('Style is not a conditional style');
1603
            }
1604
        }
1605
1606
        // Calculate range outer borders
1607
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1608
1609
        // Make sure we can loop upwards on rows and columns
1610 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 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
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1619
                $this->setConditionalStyles(Cell::stringFromColumnIndex($col - 1) . $row, $pCellStyle);
1620
            }
1621
        }
1622
1623
        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
    public function setBreak($pCell = 'A1', $pBreak = self::BREAK_NONE)
1656
    {
1657
        // Uppercase coordinate
1658
        $pCell = strtoupper($pCell);
1659
1660
        if ($pCell != '') {
1661
            if ($pBreak == self::BREAK_NONE) {
1662
                if (isset($this->breaks[$pCell])) {
1663
                    unset($this->breaks[$pCell]);
1664
                }
1665
            } else {
1666
                $this->breaks[$pCell] = $pBreak;
1667
            }
1668
        } else {
1669
            throw new Exception('No cell coordinate specified.');
1670
        }
1671
1672
        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
    public function getBreaks()
1694
    {
1695
        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
    public function mergeCells($pRange = 'A1:A1')
1706
    {
1707
        // Uppercase coordinate
1708
        $pRange = strtoupper($pRange);
1709
1710
        if (strpos($pRange, ':') !== false) {
1711
            $this->mergeCells[$pRange] = $pRange;
1712
1713
            // make sure cells are created
1714
1715
            // get the cells in the range
1716
            $aReferences = Cell::extractAllCellReferencesInRange($pRange);
1717
1718
            // create upper left cell if it does not already exist
1719
            $upperLeft = $aReferences[0];
1720
            if (!$this->cellExists($upperLeft)) {
1721
                $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
            $count = count($aReferences);
1726
            for ($i = 1; $i < $count; ++$i) {
1727
                if ($this->cellExists($aReferences[$i])) {
1728
                    $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
        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
    public function unmergeCells($pRange = 'A1:A1')
1763
    {
1764
        // Uppercase coordinate
1765
        $pRange = strtoupper($pRange);
1766
1767
        if (strpos($pRange, ':') !== false) {
1768
            if (isset($this->mergeCells[$pRange])) {
1769
                unset($this->mergeCells[$pRange]);
1770
            } else {
1771
                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
        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
    public function getMergeCells()
1803
    {
1804
        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
    public function setMergeCells($pValue = [])
1814
    {
1815
        $this->mergeCells = $pValue;
1816
1817
        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
    public function protectCells($pRange = 'A1', $pPassword = '', $pAlreadyHashed = false)
1830
    {
1831
        // Uppercase coordinate
1832
        $pRange = strtoupper($pRange);
1833
1834
        if (!$pAlreadyHashed) {
1835
            $pPassword = Shared\PasswordHasher::hashPassword($pPassword);
1836
        }
1837
        $this->protectedCells[$pRange] = $pPassword;
1838
1839
        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
    public function unprotectCells($pRange = 'A1')
1869
    {
1870
        // Uppercase coordinate
1871
        $pRange = strtoupper($pRange);
1872
1873
        if (isset($this->protectedCells[$pRange])) {
1874
            unset($this->protectedCells[$pRange]);
1875
        } else {
1876
            throw new Exception('Cell range ' . $pRange . ' not known as protected.');
1877
        }
1878
1879
        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
    public function getProtectedCells()
1907
    {
1908
        return $this->protectedCells;
1909
    }
1910
1911
    /**
1912
     *    Get Autofilter
1913
     *
1914
     *    @return Worksheet\AutoFilter
1915
     */
1916
    public function getAutoFilter()
1917
    {
1918
        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
    public function setAutoFilter($pValue)
1930
    {
1931
        $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
        if (is_string($pValue)) {
1933
            $this->autoFilter->setRange($pValue);
1934
        } elseif (is_object($pValue) && ($pValue instanceof Worksheet\AutoFilter)) {
1935
            $this->autoFilter = $pValue;
1936
        }
1937
1938
        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
    public function getFreezePane()
1978
    {
1979
        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
    public function freezePane($pCell = '')
1995
    {
1996
        // Uppercase coordinate
1997
        $pCell = strtoupper($pCell);
1998
        if (strpos($pCell, ':') === false && strpos($pCell, ',') === false) {
1999
            $this->freezePane = $pCell;
2000
        } else {
2001
            throw new Exception('Freeze pane can not be set on a range of cells.');
2002
        }
2003
2004
        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 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
        if ($pBefore >= 1) {
2041
            $objReferenceHelper = ReferenceHelper::getInstance();
2042
            $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
        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 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
        if (!is_numeric($pBefore)) {
2061
            $objReferenceHelper = ReferenceHelper::getInstance();
2062
            $objReferenceHelper->insertNewBefore($pBefore . '1', $pNumCols, 0, $this);
2063
        } else {
2064
            throw new Exception('Column references should not be numeric.');
2065
        }
2066
2067
        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
    public function removeRow($pRow = 1, $pNumRows = 1)
2096
    {
2097
        if ($pRow >= 1) {
2098
            $highestRow = $this->getHighestDataRow();
2099
            $objReferenceHelper = ReferenceHelper::getInstance();
2100
            $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
2101
            for ($r = 0; $r < $pNumRows; ++$r) {
2102
                $this->getCellCacheController()->removeRow($highestRow);
2103
                --$highestRow;
2104
            }
2105
        } else {
2106
            throw new Exception('Rows to be deleted should at least start from row 1.');
2107
        }
2108
2109
        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
    public function removeColumn($pColumn = 'A', $pNumCols = 1)
2121
    {
2122
        if (!is_numeric($pColumn)) {
2123
            $highestColumn = $this->getHighestDataColumn();
2124
            $pColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($pColumn) - 1 + $pNumCols);
2125
            $objReferenceHelper = ReferenceHelper::getInstance();
2126
            $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
2127
            for ($c = 0; $c < $pNumCols; ++$c) {
2128
                $this->getCellCacheController()->removeColumn($highestColumn);
2129
                $highestColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($highestColumn) - 2);
2130
            }
2131
        } else {
2132
            throw new Exception('Column references should not be numeric.');
2133
        }
2134
2135
        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
    public function getShowGridlines()
2161
    {
2162
        return $this->showGridlines;
2163
    }
2164
2165
    /**
2166
     * Set show gridlines
2167
     *
2168
     * @param bool $pValue    Show gridlines (true/false)
2169
     * @return Worksheet
2170
     */
2171
    public function setShowGridlines($pValue = false)
2172
    {
2173
        $this->showGridlines = $pValue;
2174
2175
        return $this;
2176
    }
2177
2178
    /**
2179
     * Print gridlines?
2180
     *
2181
     * @return bool
2182
     */
2183
    public function getPrintGridlines()
2184
    {
2185
        return $this->printGridlines;
2186
    }
2187
2188
    /**
2189
     * Set print gridlines
2190
     *
2191
     * @param bool $pValue Print gridlines (true/false)
2192
     * @return Worksheet
2193
     */
2194
    public function setPrintGridlines($pValue = false)
2195
    {
2196
        $this->printGridlines = $pValue;
2197
2198
        return $this;
2199
    }
2200
2201
    /**
2202
     * Show row and column headers?
2203
     *
2204
     * @return bool
2205
     */
2206
    public function getShowRowColHeaders()
2207
    {
2208
        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
    public function setShowRowColHeaders($pValue = false)
2218
    {
2219
        $this->showRowColHeaders = $pValue;
2220
2221
        return $this;
2222
    }
2223
2224
    /**
2225
     * Show summary below? (Row/Column outlining)
2226
     *
2227
     * @return bool
2228
     */
2229
    public function getShowSummaryBelow()
2230
    {
2231
        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
    public function setShowSummaryBelow($pValue = true)
2241
    {
2242
        $this->showSummaryBelow = $pValue;
2243
2244
        return $this;
2245
    }
2246
2247
    /**
2248
     * Show summary right? (Row/Column outlining)
2249
     *
2250
     * @return bool
2251
     */
2252
    public function getShowSummaryRight()
2253
    {
2254
        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
    public function setShowSummaryRight($pValue = true)
2264
    {
2265
        $this->showSummaryRight = $pValue;
2266
2267
        return $this;
2268
    }
2269
2270
    /**
2271
     * Get comments
2272
     *
2273
     * @return Comment[]
2274
     */
2275
    public function getComments()
2276
    {
2277
        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
    public function setComments($pValue = [])
2287
    {
2288
        $this->comments = $pValue;
2289
2290
        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
    public function getComment($pCellCoordinate = 'A1')
2301
    {
2302
        // Uppercase coordinate
2303
        $pCellCoordinate = strtoupper($pCellCoordinate);
2304
2305
        if (strpos($pCellCoordinate, ':') !== false || strpos($pCellCoordinate, ',') !== false) {
2306
            throw new Exception('Cell coordinate string can not be a range of cells.');
2307
        } elseif (strpos($pCellCoordinate, '$') !== false) {
2308
            throw new Exception('Cell coordinate string must not be absolute.');
2309
        } 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
            if (isset($this->comments[$pCellCoordinate])) {
2315
                return $this->comments[$pCellCoordinate];
2316
            } else {
2317
                $newComment = new Comment();
2318
                $this->comments[$pCellCoordinate] = $newComment;
2319
2320
                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
    public function getCommentByColumnAndRow($pColumn = 0, $pRow = 1)
2333
    {
2334
        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
    public function getActiveCell()
2354
    {
2355
        return $this->activeCell;
2356
    }
2357
2358
    /**
2359
     * Get selected cells
2360
     *
2361
     * @return string
2362
     */
2363
    public function getSelectedCells()
2364
    {
2365
        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
    public function setSelectedCells($pCoordinate = 'A1')
2387
    {
2388
        // Uppercase coordinate
2389
        $pCoordinate = strtoupper($pCoordinate);
2390
2391
        // Convert 'A' to 'A:A'
2392
        $pCoordinate = preg_replace('/^([A-Z]+)$/', '${1}:${1}', $pCoordinate);
2393
2394
        // Convert '1' to '1:1'
2395
        $pCoordinate = preg_replace('/^([0-9]+)$/', '${1}:${1}', $pCoordinate);
2396
2397
        // Convert 'A:C' to 'A1:C1048576'
2398
        $pCoordinate = preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $pCoordinate);
2399
2400
        // Convert '1:3' to 'A1:XFD3'
2401
        $pCoordinate = preg_replace('/^([0-9]+):([0-9]+)$/', 'A${1}:XFD${2}', $pCoordinate);
2402
2403
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
2404
            list($first) = Cell::splitRange($pCoordinate);
2405
            $this->activeCell = $first[0];
2406
        } else {
2407
            $this->activeCell = $pCoordinate;
2408
        }
2409
        $this->selectedCells = $pCoordinate;
2410
2411
        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
    public function getRightToLeft()
2433
    {
2434
        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
    public function setRightToLeft($value = false)
2444
    {
2445
        $this->rightToLeft = $value;
2446
2447
        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
    public function fromArray($source = null, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
2461
    {
2462
        if (is_array($source)) {
2463
            //    Convert a 1-D array to 2-D (for ease of looping)
2464
            if (!is_array(end($source))) {
2465
                $source = [$source];
2466
            }
2467
2468
            // start coordinate
2469
            list($startColumn, $startRow) = Cell::coordinateFromString($startCell);
2470
2471
            // Loop through $source
2472
            foreach ($source as $rowData) {
2473
                $currentColumn = $startColumn;
2474
                foreach ($rowData as $cellValue) {
2475
                    if ($strictNullComparison) {
2476
                        if ($cellValue !== $nullValue) {
2477
                            // Set cell value
2478
                            $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2479
                        }
2480
                    } else {
2481
                        if ($cellValue != $nullValue) {
2482
                            // Set cell value
2483
                            $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2484
                        }
2485
                    }
2486
                    ++$currentColumn;
2487
                }
2488
                ++$startRow;
2489
            }
2490
        } else {
2491
            throw new Exception('Parameter $source should be an array.');
2492
        }
2493
2494
        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
    public function rangeToArray($pRange = 'A1', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2509
    {
2510
        // Returnvalue
2511
        $returnValue = [];
2512
        //    Identify the range that we need to extract from the worksheet
2513
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange);
2514
        $minCol = Cell::stringFromColumnIndex($rangeStart[0] - 1);
2515
        $minRow = $rangeStart[1];
2516
        $maxCol = Cell::stringFromColumnIndex($rangeEnd[0] - 1);
2517
        $maxRow = $rangeEnd[1];
2518
2519
        ++$maxCol;
2520
        // Loop through rows
2521
        $r = -1;
2522
        for ($row = $minRow; $row <= $maxRow; ++$row) {
2523
            $rRef = ($returnCellRef) ? $row : ++$r;
2524
            $c = -1;
2525
            // Loop through columns in the current row
2526
            for ($col = $minCol; $col != $maxCol; ++$col) {
2527
                $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
                if ($this->cellCollection->isDataSet($col . $row)) {
2531
                    // Cell exists
2532
                    $cell = $this->cellCollection->getCacheData($col . $row);
2533
                    if ($cell->getValue() !== null) {
2534
                        if ($cell->getValue() instanceof RichText) {
2535
                            $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
2536
                        } else {
2537
                            if ($calculateFormulas) {
2538
                                $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
2539
                            } else {
2540
                                $returnValue[$rRef][$cRef] = $cell->getValue();
2541
                            }
2542
                        }
2543
2544
                        if ($formatData) {
2545
                            $style = $this->parent->getCellXfByIndex($cell->getXfIndex());
2546
                            $returnValue[$rRef][$cRef] = Style\NumberFormat::toFormattedString(
2547
                                $returnValue[$rRef][$cRef],
2548
                                ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : Style\NumberFormat::FORMAT_GENERAL
2549
                            );
2550
                        }
2551
                    } else {
2552
                        // Cell holds a NULL
2553
                        $returnValue[$rRef][$cRef] = $nullValue;
2554
                    }
2555
                } else {
2556
                    // Cell doesn't exist
2557
                    $returnValue[$rRef][$cRef] = $nullValue;
2558
                }
2559
            }
2560
        }
2561
2562
        // Return
2563
        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
    public function getRowIterator($startRow = 1, $endRow = null)
2622
    {
2623
        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
    public function garbageCollect()
2645
    {
2646
        // Flush cache
2647
        $this->cellCollection->getCacheData('A1');
2648
2649
        // Lookup highest column and highest row if cells are cleaned
2650
        $colRow = $this->cellCollection->getHighestRowAndColumn();
2651
        $highestRow = $colRow['row'];
2652
        $highestColumn = Cell::columnIndexFromString($colRow['column']);
2653
2654
        // Loop through column dimensions
2655
        foreach ($this->columnDimensions as $dimension) {
2656
            $highestColumn = max($highestColumn, Cell::columnIndexFromString($dimension->getColumnIndex()));
2657
        }
2658
2659
        // Loop through row dimensions
2660
        foreach ($this->rowDimensions as $dimension) {
2661
            $highestRow = max($highestRow, $dimension->getRowIndex());
2662
        }
2663
2664
        // Cache values
2665
        if ($highestColumn < 0) {
2666
            $this->cachedHighestColumn = 'A';
2667
        } else {
2668
            $this->cachedHighestColumn = Cell::stringFromColumnIndex(--$highestColumn);
2669
        }
2670
        $this->cachedHighestRow = $highestRow;
2671
2672
        // Return
2673
        return $this;
2674
    }
2675
2676
    /**
2677
     * Get hash code
2678
     *
2679
     * @return string    Hash code
2680
     */
2681
    public function getHashCode()
2682
    {
2683
        if ($this->dirty) {
2684
            $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__);
2685
            $this->dirty = false;
2686
        }
2687
2688
        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
    public static function extractSheetTitle($pRange, $returnRange = false)
2702
    {
2703
        // Sheet title included?
2704
        if (($sep = strpos($pRange, '!')) === false) {
2705
            return '';
2706
        }
2707
2708
        if ($returnRange) {
2709
            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 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
        if (isset($this->hyperlinkCollection[$pCellCoordinate])) {
2724
            return $this->hyperlinkCollection[$pCellCoordinate];
2725
        }
2726
2727
        // else create hyperlink
2728
        $this->hyperlinkCollection[$pCellCoordinate] = new Cell\Hyperlink();
2729
2730
        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
    public function setHyperlink($pCellCoordinate = 'A1', Cell\Hyperlink $pHyperlink = null)
2741
    {
2742
        if ($pHyperlink === null) {
2743
            unset($this->hyperlinkCollection[$pCellCoordinate]);
2744
        } else {
2745
            $this->hyperlinkCollection[$pCellCoordinate] = $pHyperlink;
2746
        }
2747
2748
        return $this;
2749
    }
2750
2751
    /**
2752
     * Hyperlink at a specific coordinate exists?
2753
     *
2754
     * @param string $pCoordinate
2755
     * @return bool
2756
     */
2757
    public function hyperlinkExists($pCoordinate = 'A1')
2758
    {
2759
        return isset($this->hyperlinkCollection[$pCoordinate]);
2760
    }
2761
2762
    /**
2763
     * Get collection of hyperlinks
2764
     *
2765
     * @return Cell\Hyperlink[]
2766
     */
2767
    public function getHyperlinkCollection()
2768
    {
2769
        return $this->hyperlinkCollection;
2770
    }
2771
2772
    /**
2773
     * Get data validation
2774
     *
2775
     * @param string $pCellCoordinate Cell coordinate to get data validation for
2776
     */
2777 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
        if (isset($this->dataValidationCollection[$pCellCoordinate])) {
2781
            return $this->dataValidationCollection[$pCellCoordinate];
2782
        }
2783
2784
        // else create data validation
2785
        $this->dataValidationCollection[$pCellCoordinate] = new Cell\DataValidation();
2786
2787
        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
    public function getDataValidationCollection()
2825
    {
2826
        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
    public function getTabColor()
2871
    {
2872
        if ($this->tabColor === null) {
2873
            $this->tabColor = new Style\Color();
2874
        }
2875
2876
        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
    public function isTabColorSet()
2898
    {
2899
        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
    public function __clone()
2918
    {
2919
        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
            if ($key == 'parent') {
2921
                continue;
2922
            }
2923
2924
            if (is_object($val) || (is_array($val))) {
2925
                if ($key == 'cellCollection') {
2926
                    $newCollection = clone $this->cellCollection;
2927
                    $newCollection->copyCellCollection($this);
2928
                    $this->cellCollection = $newCollection;
2929
                } elseif ($key == 'drawingCollection') {
2930
                    $newCollection = clone $this->drawingCollection;
2931
                    $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
                } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof Worksheet\AutoFilter)) {
2933
                    $newAutoFilter = clone $this->autoFilter;
2934
                    $this->autoFilter = $newAutoFilter;
2935
                    $this->autoFilter->setParent($this);
2936
                } else {
2937
                    $this->{$key} = unserialize(serialize($val));
2938
                }
2939
            }
2940
        }
2941
    }
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
    public function setCodeName($pValue = null)
2950
    {
2951 9
        // Is this a 'rename' or not?
2952
        if ($this->getCodeName() == $pValue) {
2953
            return $this;
2954 9
        }
2955
        $pValue = str_replace(' ', '_', $pValue); //Excel does this automatically without flinching, we are doing the same
2956
        // Syntax check
2957 9
        // throw an exception if not valid
2958
        self::checkSheetCodeName($pValue);
2959
2960 9
        // 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
        if ($this->getParent()) {
2963
            // Is there already such sheet name?
2964 9
            if ($this->getParent()->sheetCodeNameExists($pValue)) {
2965
                // Use name, but append with lowest possible integer
2966 1
2967
                if (Shared\StringHelper::countCharacters($pValue) > 29) {
2968
                    $pValue = Shared\StringHelper::substring($pValue, 0, 29);
2969
                }
2970
                $i = 1;
2971 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
                    ++$i;
2973
                    if ($i == 10) {
2974
                        if (Shared\StringHelper::countCharacters($pValue) > 28) {
2975
                            $pValue = Shared\StringHelper::substring($pValue, 0, 28);
2976
                        }
2977
                    } elseif ($i == 100) {
2978
                        if (Shared\StringHelper::countCharacters($pValue) > 27) {
2979
                            $pValue = Shared\StringHelper::substring($pValue, 0, 27);
2980
                        }
2981
                    }
2982
                }
2983
2984
                $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
        $this->codeName = $pValue;
2991
2992 9
        return $this;
2993
    }
2994 9
    /**
2995
     * Return the code name of the sheet
2996
     *
2997
     * @return null|string
2998
     */
2999
    public function getCodeName()
3000
    {
3001 9
        return $this->codeName;
3002
    }
3003 9
    /**
3004
     * Sheet has a code name ?
3005
     * @return bool
3006
     */
3007
    public function hasCodeName()
3008
    {
3009
        return !(is_null($this->codeName));
3010
    }
3011
}
3012