Completed
Push — develop ( 6d8ba6...939f24 )
by Adrien
23:45
created

Worksheet::getHighestDataColumn()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
342
        // Set page setup
343 70
        $this->pageSetup = new Worksheet\PageSetup();
344
        // Set page margins
345 70
        $this->pageMargins = new Worksheet\PageMargins();
346
        // Set page header/footer
347 70
        $this->headerFooter = new Worksheet\HeaderFooter();
348
        // Set sheet view
349 70
        $this->sheetView = new Worksheet\SheetView();
350
        // Drawing collection
351 70
        $this->drawingCollection = new \ArrayObject();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \ArrayObject() of type object<ArrayObject> is incompatible with the declared type array<integer,object<Php...Worksheet\BaseDrawing>> of property $drawingCollection.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
352
        // Chart collection
353 70
        $this->chartCollection = new \ArrayObject();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \ArrayObject() of type object<ArrayObject> is incompatible with the declared type array<integer,object<Php...\PhpSpreadsheet\Chart>> of property $chartCollection.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

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

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

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

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

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

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

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
560 14
        if (is_null($iChartIndex)) {
561 14
            $this->chartCollection[] = $pChart;
562
        } else {
563
            // Insert the chart at the requested index
564
            array_splice($this->chartCollection, $iChartIndex, 0, [$pChart]);
565
        }
566
567 14
        return $pChart;
568
    }
569
570
    /**
571
     * Return the count of charts on this worksheet
572
     *
573
     * @return int        The number of charts
574
     */
575 14
    public function getChartCount()
576
    {
577 14
        return count($this->chartCollection);
578
    }
579
580
    /**
581
     * Get a chart by its index position
582
     *
583
     * @param string $index Chart index position
584
     * @throws Exception
585
     * @return false|Chart
586
     */
587 13
    public function getChartByIndex($index = null)
588
    {
589 13
        $chartCount = count($this->chartCollection);
590 13
        if ($chartCount == 0) {
591
            return false;
592
        }
593 13
        if (is_null($index)) {
594
            $index = --$chartCount;
595
        }
596 13
        if (!isset($this->chartCollection[$index])) {
597
            return false;
598
        }
599
600 13
        return $this->chartCollection[$index];
601
    }
602
603
    /**
604
     * Return an array of the names of charts on this worksheet
605
     *
606
     * @throws Exception
607
     * @return string[] The names of charts
608
     */
609 1
    public function getChartNames()
610
    {
611 1
        $chartNames = [];
612 1
        foreach ($this->chartCollection as $chart) {
613 1
            $chartNames[] = $chart->getName();
614
        }
615
616 1
        return $chartNames;
617
    }
618
619
    /**
620
     * Get a chart by name
621
     *
622
     * @param string $chartName Chart name
623
     * @throws Exception
624
     * @return false|Chart
625
     */
626 1
    public function getChartByName($chartName = '')
627
    {
628 1
        $chartCount = count($this->chartCollection);
629 1
        if ($chartCount == 0) {
630
            return false;
631
        }
632 1
        foreach ($this->chartCollection as $index => $chart) {
633 1
            if ($chart->getName() == $chartName) {
634 1
                return $this->chartCollection[$index];
635
            }
636
        }
637
638
        return false;
639
    }
640
641
    /**
642
     * Refresh column dimensions
643
     *
644
     * @return Worksheet
645
     */
646 12
    public function refreshColumnDimensions()
647
    {
648 12
        $currentColumnDimensions = $this->getColumnDimensions();
649 12
        $newColumnDimensions = [];
650
651 12
        foreach ($currentColumnDimensions as $objColumnDimension) {
652 12
            $newColumnDimensions[$objColumnDimension->getColumnIndex()] = $objColumnDimension;
653
        }
654
655 12
        $this->columnDimensions = $newColumnDimensions;
656
657 12
        return $this;
658
    }
659
660
    /**
661
     * Refresh row dimensions
662
     *
663
     * @return Worksheet
664
     */
665 2
    public function refreshRowDimensions()
666
    {
667 2
        $currentRowDimensions = $this->getRowDimensions();
668 2
        $newRowDimensions = [];
669
670 2
        foreach ($currentRowDimensions as $objRowDimension) {
671 2
            $newRowDimensions[$objRowDimension->getRowIndex()] = $objRowDimension;
672
        }
673
674 2
        $this->rowDimensions = $newRowDimensions;
675
676 2
        return $this;
677
    }
678
679
    /**
680
     * Calculate worksheet dimension
681
     *
682
     * @return string  String containing the dimension of this worksheet
683
     */
684 58
    public function calculateWorksheetDimension()
685
    {
686
        // Return
687 58
        return 'A1' . ':' . $this->getHighestColumn() . $this->getHighestRow();
688
    }
689
690
    /**
691
     * Calculate worksheet data dimension
692
     *
693
     * @return string  String containing the dimension of this worksheet that actually contain data
694
     */
695
    public function calculateWorksheetDataDimension()
696
    {
697
        // Return
698
        return 'A1' . ':' . $this->getHighestDataColumn() . $this->getHighestDataRow();
699
    }
700
701
    /**
702
     * Calculate widths for auto-size columns
703
     *
704
     * @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...
705
     */
706 43
    public function calculateColumnWidths()
707
    {
708
        // initialize $autoSizes array
709 43
        $autoSizes = [];
710 43
        foreach ($this->getColumnDimensions() as $colDimension) {
711 23
            if ($colDimension->getAutoSize()) {
712 23
                $autoSizes[$colDimension->getColumnIndex()] = -1;
713
            }
714
        }
715
716
        // There is only something to do if there are some auto-size columns
717 43
        if (!empty($autoSizes)) {
718
            // build list of cells references that participate in a merge
719 11
            $isMergeCell = [];
720 11
            foreach ($this->getMergeCells() as $cells) {
721 8
                foreach (Cell::extractAllCellReferencesInRange($cells) as $cellReference) {
0 ignored issues
show
Documentation introduced by
$cells is of type array, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
722 8
                    $isMergeCell[$cellReference] = true;
723
                }
724
            }
725
726
            // loop through all cells in the worksheet
727 11
            foreach ($this->getCellCollection(false) as $cellID) {
728 11
                $cell = $this->getCell($cellID, false);
729 11
                if ($cell !== null && isset($autoSizes[$this->cellCollection->getCurrentColumn()])) {
730
                    //Determine if cell is in merge range
731 11
                    $isMerged = isset($isMergeCell[$this->cellCollection->getCurrentAddress()]);
732
733
                    //By default merged cells should be ignored
734 11
                    $isMergedButProceed = false;
735
736
                    //The only exception is if it's a merge range value cell of a 'vertical' randge (1 column wide)
737 11
                    if ($isMerged && $cell->isMergeRangeValueCell()) {
738
                        $range = $cell->getMergeRange();
739
                        $rangeBoundaries = Cell::rangeDimension($range);
0 ignored issues
show
Documentation introduced by
$range is of type array|false, but the function expects a string.

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

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

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

function acceptsInteger($int) { }

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

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
740
                        if ($rangeBoundaries[0] == 1) {
741
                            $isMergedButProceed = true;
742
                        }
743
                    }
744
745
                    // Determine width if cell does not participate in a merge or does and is a value cell of 1-column wide range
746 11
                    if (!$isMerged || $isMergedButProceed) {
747
                        // Calculated value
748
                        // To formatted string
749 11
                        $cellValue = Style\NumberFormat::toFormattedString(
750 11
                            $cell->getCalculatedValue(),
751 11
                            $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getNumberFormat()->getFormatCode()
752
                        );
753
754 11
                        $autoSizes[$this->cellCollection->getCurrentColumn()] = max(
755 11
                            (float) $autoSizes[$this->cellCollection->getCurrentColumn()],
756 11
                            (float) Shared\Font::calculateColumnWidth(
757 11
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getFont(),
758
                                $cellValue,
759 11
                                $this->getParent()->getCellXfByIndex($cell->getXfIndex())->getAlignment()->getTextRotation(),
760 11
                                $this->getDefaultStyle()->getFont()
0 ignored issues
show
Deprecated Code introduced by
The method PhpOffice\PhpSpreadsheet...heet::getDefaultStyle() has been deprecated.

This method has been deprecated.

Loading history...
761
                            )
762
                        );
763
                    }
764
                }
765
            }
766
767
            // adjust column widths
768 11
            foreach ($autoSizes as $columnIndex => $width) {
769 11
                if ($width == -1) {
770
                    $width = $this->getDefaultColumnDimension()->getWidth();
771
                }
772 11
                $this->getColumnDimension($columnIndex)->setWidth($width);
773
            }
774
        }
775
776 43
        return $this;
777
    }
778
779
    /**
780
     * Get parent
781
     *
782
     * @return Spreadsheet
783
     */
784 70
    public function getParent()
785
    {
786 70
        return $this->parent;
787
    }
788
789
    /**
790
     * Re-bind parent
791
     *
792
     * @param Spreadsheet $parent
793
     * @return Worksheet
794
     */
795 1
    public function rebindParent(Spreadsheet $parent)
796
    {
797 1
        if ($this->parent !== null) {
798 1
            $namedRanges = $this->parent->getNamedRanges();
799 1
            foreach ($namedRanges as $namedRange) {
800
                $parent->addNamedRange($namedRange);
801
            }
802
803 1
            $this->parent->removeSheetByIndex(
804 1
                $this->parent->getIndex($this)
805
            );
806
        }
807 1
        $this->parent = $parent;
808
809 1
        return $this;
810
    }
811
812
    /**
813
     * Get title
814
     *
815
     * @return string
816
     */
817 70
    public function getTitle()
818
    {
819 70
        return $this->title;
820
    }
821
822
    /**
823
     * Set title
824
     *
825
     * @param string $pValue String containing the dimension of this worksheet
826
     * @param string $updateFormulaCellReferences boolean Flag indicating whether cell references in formulae should
827
     *            be updated to reflect the new sheet name.
828
     *          This should be left as the default true, unless you are
829
     *          certain that no formula cells on any worksheet contain
830
     *          references to this worksheet
831
     * @return Worksheet
832
     */
833 70
    public function setTitle($pValue = 'Worksheet', $updateFormulaCellReferences = true)
834
    {
835
        // Is this a 'rename' or not?
836 70
        if ($this->getTitle() == $pValue) {
837 4
            return $this;
838
        }
839
840
        // Syntax check
841 70
        self::checkSheetTitle($pValue);
842
843
        // Old title
844 70
        $oldTitle = $this->getTitle();
845
846 70
        if ($this->parent) {
847
            // Is there already such sheet name?
848 62
            if ($this->parent->sheetNameExists($pValue)) {
849
                // Use name, but append with lowest possible integer
850
851 2
                if (Shared\StringHelper::countCharacters($pValue) > 29) {
852
                    $pValue = Shared\StringHelper::substring($pValue, 0, 29);
853
                }
854 2
                $i = 1;
855 2 View Code Duplication
                while ($this->parent->sheetNameExists($pValue . ' ' . $i)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

Loading history...
1179 1
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1180
1181 1
            return $this->parent->getSheetByName($worksheetReference[0])->getCell(strtoupper($worksheetReference[1]), $createIfNotExists);
1182
        }
1183
1184
        // Named range?
1185 61
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1186 61
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1187
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1188
            if ($namedRange !== null) {
1189
                $pCoordinate = $namedRange->getRange();
1190
1191
                return $namedRange->getWorksheet()->getCell($pCoordinate, $createIfNotExists);
1192
            }
1193
        }
1194
1195
        // Uppercase coordinate
1196 61
        $pCoordinate = strtoupper($pCoordinate);
1197
1198 61 View Code Duplication
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1199
            throw new Exception('Cell coordinate can not be a range of cells.');
1200 61
        } elseif (strpos($pCoordinate, '$') !== false) {
1201
            throw new Exception('Cell coordinate must not be absolute.');
1202
        }
1203
1204
        // Create new cell object, if required
1205 61
        return $createIfNotExists ? $this->createNewCell($pCoordinate) : null;
1206
    }
1207
1208
    /**
1209
     * Get cell at a specific coordinate by using numeric cell coordinates
1210
     *
1211
     * @param  string $pColumn Numeric column coordinate of the cell
1212
     * @param string $pRow Numeric row coordinate of the cell
1213
     * @param bool $createIfNotExists  Flag indicating whether a new cell should be created if it doesn't
1214
     *                                       already exist, or a null should be returned instead
1215
     * @return null|Cell Cell that was found/created or null
1216
     */
1217 29
    public function getCellByColumnAndRow($pColumn = 0, $pRow = 1, $createIfNotExists = true)
1218
    {
1219 29
        $columnLetter = Cell::stringFromColumnIndex($pColumn);
1220 29
        $coordinate = $columnLetter . $pRow;
1221
1222 29
        if ($this->cellCollection->isDataSet($coordinate)) {
1223 29
            return $this->cellCollection->getCacheData($coordinate);
1224
        }
1225
1226
        // Create new cell object, if required
1227 17
        return $createIfNotExists ? $this->createNewCell($coordinate) : null;
1228
    }
1229
1230
    /**
1231
     * Create a new cell at the specified coordinate
1232
     *
1233
     * @param string $pCoordinate    Coordinate of the cell
1234
     * @return Cell Cell that was created
1235
     */
1236 62
    private function createNewCell($pCoordinate)
1237
    {
1238 62
        $cell = $this->cellCollection->addCacheData(
1239
            $pCoordinate,
1240 62
            new Cell(null, Cell\DataType::TYPE_NULL, $this)
1241
        );
1242 62
        $this->cellCollectionIsSorted = false;
1243
1244
        // Coordinates
1245 62
        $aCoordinates = Cell::coordinateFromString($pCoordinate);
1246 62
        if (Cell::columnIndexFromString($this->cachedHighestColumn) < Cell::columnIndexFromString($aCoordinates[0])) {
1247 54
            $this->cachedHighestColumn = $aCoordinates[0];
1248
        }
1249 62
        $this->cachedHighestRow = max($this->cachedHighestRow, $aCoordinates[1]);
0 ignored issues
show
Documentation Bug introduced by
It seems like max($this->cachedHighestRow, $aCoordinates[1]) can also be of type string. However, the property $cachedHighestRow is declared as type integer. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
1250
1251
        // Cell needs appropriate xfIndex from dimensions records
1252
        //    but don't create dimension records if they don't already exist
1253 62
        $rowDimension = $this->getRowDimension($aCoordinates[1], false);
1254 62
        $columnDimension = $this->getColumnDimension($aCoordinates[0], false);
1255
1256 62
        if ($rowDimension !== null && $rowDimension->getXfIndex() > 0) {
1257
            // then there is a row dimension with explicit style, assign it to the cell
1258
            $cell->setXfIndex($rowDimension->getXfIndex());
1259 62
        } elseif ($columnDimension !== null && $columnDimension->getXfIndex() > 0) {
1260
            // then there is a column dimension, assign it to the cell
1261
            $cell->setXfIndex($columnDimension->getXfIndex());
1262
        }
1263
1264 62
        return $cell;
1265
    }
1266
1267
    /**
1268
     * Does the cell at a specific coordinate exist?
1269
     *
1270
     * @param string $pCoordinate  Coordinate of the cell
1271
     * @throws Exception
1272
     * @return bool
1273
     */
1274 37
    public function cellExists($pCoordinate = 'A1')
1275
    {
1276
        // Worksheet reference?
1277 37 View Code Duplication
        if (strpos($pCoordinate, '!') !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1278
            $worksheetReference = self::extractSheetTitle($pCoordinate, true);
1279
1280
            return $this->parent->getSheetByName($worksheetReference[0])->cellExists(strtoupper($worksheetReference[1]));
1281
        }
1282
1283
        // Named range?
1284 37
        if ((!preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $pCoordinate, $matches)) &&
1285 37
            (preg_match('/^' . Calculation::CALCULATION_REGEXP_NAMEDRANGE . '$/i', $pCoordinate, $matches))) {
1286
            $namedRange = NamedRange::resolveRange($pCoordinate, $this);
1287
            if ($namedRange !== null) {
1288
                $pCoordinate = $namedRange->getRange();
1289
                if ($this->getHashCode() != $namedRange->getWorksheet()->getHashCode()) {
1290
                    if (!$namedRange->getLocalOnly()) {
1291
                        return $namedRange->getWorksheet()->cellExists($pCoordinate);
1292
                    } else {
1293
                        throw new Exception('Named range ' . $namedRange->getName() . ' is not accessible from within sheet ' . $this->getTitle());
1294
                    }
1295
                }
1296
            } else {
1297
                return false;
1298
            }
1299
        }
1300
1301
        // Uppercase coordinate
1302 37
        $pCoordinate = strtoupper($pCoordinate);
1303
1304 37
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
1305
            throw new Exception('Cell coordinate can not be a range of cells.');
1306 37
        } elseif (strpos($pCoordinate, '$') !== false) {
1307
            throw new Exception('Cell coordinate must not be absolute.');
1308
        } else {
1309
            // Coordinates
1310 37
            $aCoordinates = Cell::coordinateFromString($pCoordinate);
0 ignored issues
show
Unused Code introduced by
$aCoordinates is not used, you could remove the assignment.

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

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

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

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

Loading history...
1311
1312
            // Cell exists?
1313 37
            return $this->cellCollection->isDataSet($pCoordinate);
1314
        }
1315
    }
1316
1317
    /**
1318
     * Cell at a specific coordinate by using numeric cell coordinates exists?
1319
     *
1320
     * @param string $pColumn Numeric column coordinate of the cell
1321
     * @param string $pRow Numeric row coordinate of the cell
1322
     * @return bool
1323
     */
1324 3
    public function cellExistsByColumnAndRow($pColumn = 0, $pRow = 1)
1325
    {
1326 3
        return $this->cellExists(Cell::stringFromColumnIndex($pColumn) . $pRow);
1327
    }
1328
1329
    /**
1330
     * Get row dimension at a specific row
1331
     *
1332
     * @param int $pRow Numeric index of the row
1333
     * @return Worksheet\RowDimension
1334
     */
1335 62
    public function getRowDimension($pRow = 1, $create = true)
1336
    {
1337
        // Found
1338 62
        $found = null;
0 ignored issues
show
Unused Code introduced by
$found is not used, you could remove the assignment.

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

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

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

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

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

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

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
Unused Code introduced by
$style is not used, you could remove the assignment.

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

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

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

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

Loading history...
1570
1571
        // Add the style to the workbook if necessary
1572 2
        $workbook = $this->parent;
1573 2
        if ($existingStyle = $this->parent->getCellXfByHashCode($pCellStyle->getHashCode())) {
1574
            // there is already such cell Xf in our collection
1575 1
            $xfIndex = $existingStyle->getIndex();
1576
        } else {
1577
            // we don't have such a cell Xf, need to add
1578 2
            $workbook->addCellXf($pCellStyle);
0 ignored issues
show
Bug introduced by
It seems like $pCellStyle defined by parameter $pCellStyle on line 1566 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...
1579 2
            $xfIndex = $pCellStyle->getIndex();
1580
        }
1581
1582
        // Calculate range outer borders
1583 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1584
1585
        // Make sure we can loop upwards on rows and columns
1586 2 View Code Duplication
        if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1587
            $tmp = $rangeStart;
1588
            $rangeStart = $rangeEnd;
1589
            $rangeEnd = $tmp;
1590
        }
1591
1592
        // Loop through cells and apply styles
1593 2 View Code Duplication
        for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1594 2
            for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
1595 2
                $this->getCell(Cell::stringFromColumnIndex($col - 1) . $row)->setXfIndex($xfIndex);
1596
            }
1597
        }
1598
1599 2
        return $this;
1600
    }
1601
1602
    /**
1603
     * Duplicate conditional style to a range of cells
1604
     *
1605
     * Please note that this will overwrite existing cell styles for cells in range!
1606
     *
1607
     * @param    Style\Conditional[]    $pCellStyle    Cell style to duplicate
1608
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
1609
     * @throws Exception
1610
     * @return Worksheet
1611
     */
1612 2
    public function duplicateConditionalStyle(array $pCellStyle = null, $pRange = '')
1613
    {
1614 2
        foreach ($pCellStyle as $cellStyle) {
0 ignored issues
show
Bug introduced by
The expression $pCellStyle of type null|array<integer,objec...eet\Style\Conditional>> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

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

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

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

Loading history...
1615 2
            if (!($cellStyle instanceof Style\Conditional)) {
1616 2
                throw new Exception('Style is not a conditional style');
1617
            }
1618
        }
1619
1620
        // Calculate range outer borders
1621 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange . ':' . $pRange);
1622
1623
        // Make sure we can loop upwards on rows and columns
1624 2 View Code Duplication
        if ($rangeStart[0] > $rangeEnd[0] && $rangeStart[1] > $rangeEnd[1]) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
1625
            $tmp = $rangeStart;
1626
            $rangeStart = $rangeEnd;
1627
            $rangeEnd = $tmp;
1628
        }
1629
1630
        // Loop through cells and apply styles
1631 2 View Code Duplication
        for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

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

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

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

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

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

Loading history...
1946 4
        if (is_string($pValue)) {
1947 4
            $this->autoFilter->setRange($pValue);
1948
        } elseif (is_object($pValue) && ($pValue instanceof Worksheet\AutoFilter)) {
1949
            $this->autoFilter = $pValue;
1950
        }
1951
1952 4
        return $this;
1953
    }
1954
1955
    /**
1956
     *    Set Autofilter Range by using numeric cell coordinates
1957
     *
1958
     *    @param  int  $pColumn1    Numeric column coordinate of the first cell
1959
     *    @param  int  $pRow1       Numeric row coordinate of the first cell
1960
     *    @param  int  $pColumn2    Numeric column coordinate of the second cell
1961
     *    @param  int  $pRow2       Numeric row coordinate of the second cell
1962
     *    @throws    Exception
1963
     *    @return Worksheet
1964
     */
1965
    public function setAutoFilterByColumnAndRow($pColumn1 = 0, $pRow1 = 1, $pColumn2 = 0, $pRow2 = 1)
1966
    {
1967
        return $this->setAutoFilter(
1968
            Cell::stringFromColumnIndex($pColumn1) . $pRow1
1969
            . ':' .
1970
            Cell::stringFromColumnIndex($pColumn2) . $pRow2
1971
        );
1972
    }
1973
1974
    /**
1975
     * Remove autofilter
1976
     *
1977
     * @return Worksheet
1978
     */
1979
    public function removeAutoFilter()
1980
    {
1981
        $this->autoFilter->setRange(null);
1982
1983
        return $this;
1984
    }
1985
1986
    /**
1987
     * Get Freeze Pane
1988
     *
1989
     * @return string
1990
     */
1991 59
    public function getFreezePane()
1992
    {
1993 59
        return $this->freezePane;
1994
    }
1995
1996
    /**
1997
     * Freeze Pane
1998
     *
1999
     * @param    string        $pCell        Cell (i.e. A2)
2000
     *                                    Examples:
2001
     *                                        A2 will freeze the rows above cell A2 (i.e row 1)
2002
     *                                        B1 will freeze the columns to the left of cell B1 (i.e column A)
2003
     *                                        B2 will freeze the rows above and to the left of cell A2
2004
     *                                            (i.e row 1 and column A)
2005
     * @throws    Exception
2006
     * @return Worksheet
2007
     */
2008 4
    public function freezePane($pCell = '')
2009
    {
2010
        // Uppercase coordinate
2011 4
        $pCell = strtoupper($pCell);
2012 4
        if (strpos($pCell, ':') === false && strpos($pCell, ',') === false) {
2013 4
            $this->freezePane = $pCell;
2014
        } else {
2015
            throw new Exception('Freeze pane can not be set on a range of cells.');
2016
        }
2017
2018 4
        return $this;
2019
    }
2020
2021
    /**
2022
     * Freeze Pane by using numeric cell coordinates
2023
     *
2024
     * @param int $pColumn    Numeric column coordinate of the cell
2025
     * @param int $pRow        Numeric row coordinate of the cell
2026
     * @throws    Exception
2027
     * @return Worksheet
2028
     */
2029
    public function freezePaneByColumnAndRow($pColumn = 0, $pRow = 1)
2030
    {
2031
        return $this->freezePane(Cell::stringFromColumnIndex($pColumn) . $pRow);
2032
    }
2033
2034
    /**
2035
     * Unfreeze Pane
2036
     *
2037
     * @return Worksheet
2038
     */
2039
    public function unfreezePane()
2040
    {
2041
        return $this->freezePane('');
2042
    }
2043
2044
    /**
2045
     * Insert a new row, updating all possible related data
2046
     *
2047
     * @param int $pBefore    Insert before this one
2048
     * @param int $pNumRows    Number of rows to insert
2049
     * @throws    Exception
2050
     * @return Worksheet
2051
     */
2052 10 View Code Duplication
    public function insertNewRowBefore($pBefore = 1, $pNumRows = 1)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

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

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

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

Loading history...
2073
    {
2074 9
        if (!is_numeric($pBefore)) {
2075 9
            $objReferenceHelper = ReferenceHelper::getInstance();
2076 9
            $objReferenceHelper->insertNewBefore($pBefore . '1', $pNumCols, 0, $this);
2077
        } else {
2078
            throw new Exception('Column references should not be numeric.');
2079
        }
2080
2081 9
        return $this;
2082
    }
2083
2084
    /**
2085
     * Insert a new column, updating all possible related data
2086
     *
2087
     * @param int $pBefore    Insert before this one (numeric column coordinate of the cell)
2088
     * @param int $pNumCols    Number of columns to insert
2089
     * @throws    Exception
2090
     * @return Worksheet
2091
     */
2092
    public function insertNewColumnBeforeByIndex($pBefore = 0, $pNumCols = 1)
2093
    {
2094
        if ($pBefore >= 0) {
2095
            return $this->insertNewColumnBefore(Cell::stringFromColumnIndex($pBefore), $pNumCols);
2096
        } else {
2097
            throw new Exception('Columns can only be inserted before at least column A (0).');
2098
        }
2099
    }
2100
2101
    /**
2102
     * Delete a row, updating all possible related data
2103
     *
2104
     * @param int $pRow        Remove starting with this one
2105
     * @param int $pNumRows    Number of rows to remove
2106
     * @throws    Exception
2107
     * @return Worksheet
2108
     */
2109 12
    public function removeRow($pRow = 1, $pNumRows = 1)
2110
    {
2111 12
        if ($pRow >= 1) {
2112 12
            $highestRow = $this->getHighestDataRow();
2113 12
            $objReferenceHelper = ReferenceHelper::getInstance();
2114 12
            $objReferenceHelper->insertNewBefore('A' . ($pRow + $pNumRows), 0, -$pNumRows, $this);
2115 12
            for ($r = 0; $r < $pNumRows; ++$r) {
2116 12
                $this->getCellCacheController()->removeRow($highestRow);
2117 12
                --$highestRow;
2118
            }
2119
        } else {
2120
            throw new Exception('Rows to be deleted should at least start from row 1.');
2121
        }
2122
2123 12
        return $this;
2124
    }
2125
2126
    /**
2127
     * Remove a column, updating all possible related data
2128
     *
2129
     * @param string    $pColumn     Remove starting with this one
2130
     * @param int       $pNumCols    Number of columns to remove
2131
     * @throws    Exception
2132
     * @return Worksheet
2133
     */
2134 9
    public function removeColumn($pColumn = 'A', $pNumCols = 1)
2135
    {
2136 9
        if (!is_numeric($pColumn)) {
2137 9
            $highestColumn = $this->getHighestDataColumn();
2138 9
            $pColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($pColumn) - 1 + $pNumCols);
2139 9
            $objReferenceHelper = ReferenceHelper::getInstance();
2140 9
            $objReferenceHelper->insertNewBefore($pColumn . '1', -$pNumCols, 0, $this);
2141 9
            for ($c = 0; $c < $pNumCols; ++$c) {
2142 9
                $this->getCellCacheController()->removeColumn($highestColumn);
2143 9
                $highestColumn = Cell::stringFromColumnIndex(Cell::columnIndexFromString($highestColumn) - 2);
2144
            }
2145
        } else {
2146
            throw new Exception('Column references should not be numeric.');
2147
        }
2148
2149 9
        return $this;
2150
    }
2151
2152
    /**
2153
     * Remove a column, updating all possible related data
2154
     *
2155
     * @param int $pColumn    Remove starting with this one (numeric column coordinate of the cell)
2156
     * @param int $pNumCols    Number of columns to remove
2157
     * @throws    Exception
2158
     * @return Worksheet
2159
     */
2160
    public function removeColumnByIndex($pColumn = 0, $pNumCols = 1)
2161
    {
2162
        if ($pColumn >= 0) {
2163
            return $this->removeColumn(Cell::stringFromColumnIndex($pColumn), $pNumCols);
2164
        } else {
2165
            throw new Exception('Columns to be deleted should at least start from column 0');
2166
        }
2167
    }
2168
2169
    /**
2170
     * Show gridlines?
2171
     *
2172
     * @return bool
2173
     */
2174 59
    public function getShowGridlines()
2175
    {
2176 59
        return $this->showGridlines;
2177
    }
2178
2179
    /**
2180
     * Set show gridlines
2181
     *
2182
     * @param bool $pValue    Show gridlines (true/false)
2183
     * @return Worksheet
2184
     */
2185 9
    public function setShowGridlines($pValue = false)
2186
    {
2187 9
        $this->showGridlines = $pValue;
2188
2189 9
        return $this;
2190
    }
2191
2192
    /**
2193
     * Print gridlines?
2194
     *
2195
     * @return bool
2196
     */
2197 58
    public function getPrintGridlines()
2198
    {
2199 58
        return $this->printGridlines;
2200
    }
2201
2202
    /**
2203
     * Set print gridlines
2204
     *
2205
     * @param bool $pValue Print gridlines (true/false)
2206
     * @return Worksheet
2207
     */
2208 4
    public function setPrintGridlines($pValue = false)
2209
    {
2210 4
        $this->printGridlines = $pValue;
2211
2212 4
        return $this;
2213
    }
2214
2215
    /**
2216
     * Show row and column headers?
2217
     *
2218
     * @return bool
2219
     */
2220 58
    public function getShowRowColHeaders()
2221
    {
2222 58
        return $this->showRowColHeaders;
2223
    }
2224
2225
    /**
2226
     * Set show row and column headers
2227
     *
2228
     * @param bool $pValue Show row and column headers (true/false)
2229
     * @return Worksheet
2230
     */
2231 9
    public function setShowRowColHeaders($pValue = false)
2232
    {
2233 9
        $this->showRowColHeaders = $pValue;
2234
2235 9
        return $this;
2236
    }
2237
2238
    /**
2239
     * Show summary below? (Row/Column outlining)
2240
     *
2241
     * @return bool
2242
     */
2243 58
    public function getShowSummaryBelow()
2244
    {
2245 58
        return $this->showSummaryBelow;
2246
    }
2247
2248
    /**
2249
     * Set show summary below
2250
     *
2251
     * @param bool $pValue    Show summary below (true/false)
2252
     * @return Worksheet
2253
     */
2254 9
    public function setShowSummaryBelow($pValue = true)
2255
    {
2256 9
        $this->showSummaryBelow = $pValue;
2257
2258 9
        return $this;
2259
    }
2260
2261
    /**
2262
     * Show summary right? (Row/Column outlining)
2263
     *
2264
     * @return bool
2265
     */
2266 58
    public function getShowSummaryRight()
2267
    {
2268 58
        return $this->showSummaryRight;
2269
    }
2270
2271
    /**
2272
     * Set show summary right
2273
     *
2274
     * @param bool $pValue    Show summary right (true/false)
2275
     * @return Worksheet
2276
     */
2277 9
    public function setShowSummaryRight($pValue = true)
2278
    {
2279 9
        $this->showSummaryRight = $pValue;
2280
2281 9
        return $this;
2282
    }
2283
2284
    /**
2285
     * Get comments
2286
     *
2287
     * @return Comment[]
2288
     */
2289 58
    public function getComments()
2290
    {
2291 58
        return $this->comments;
2292
    }
2293
2294
    /**
2295
     * Set comments array for the entire sheet.
2296
     *
2297
     * @param array of Comment
2298
     * @return Worksheet
2299
     */
2300 12
    public function setComments($pValue = [])
2301
    {
2302 12
        $this->comments = $pValue;
2303
2304 12
        return $this;
2305
    }
2306
2307
    /**
2308
     * Get comment for cell
2309
     *
2310
     * @param string $pCellCoordinate    Cell coordinate to get comment for
2311
     * @throws Exception
2312
     * @return Comment
2313
     */
2314 13
    public function getComment($pCellCoordinate = 'A1')
2315
    {
2316
        // Uppercase coordinate
2317 13
        $pCellCoordinate = strtoupper($pCellCoordinate);
2318
2319 13
        if (strpos($pCellCoordinate, ':') !== false || strpos($pCellCoordinate, ',') !== false) {
2320
            throw new Exception('Cell coordinate string can not be a range of cells.');
2321 13
        } elseif (strpos($pCellCoordinate, '$') !== false) {
2322
            throw new Exception('Cell coordinate string must not be absolute.');
2323 13
        } elseif ($pCellCoordinate == '') {
2324
            throw new Exception('Cell coordinate can not be zero-length string.');
2325
        } else {
2326
            // Check if we already have a comment for this cell.
2327
            // If not, create a new comment.
2328 13
            if (isset($this->comments[$pCellCoordinate])) {
2329 9
                return $this->comments[$pCellCoordinate];
2330
            } else {
2331 13
                $newComment = new Comment();
2332 13
                $this->comments[$pCellCoordinate] = $newComment;
2333
2334 13
                return $newComment;
2335
            }
2336
        }
2337
    }
2338
2339
    /**
2340
     * Get comment for cell by using numeric cell coordinates
2341
     *
2342
     * @param int $pColumn    Numeric column coordinate of the cell
2343
     * @param int $pRow        Numeric row coordinate of the cell
2344
     * @return Comment
2345
     */
2346 2
    public function getCommentByColumnAndRow($pColumn = 0, $pRow = 1)
2347
    {
2348 2
        return $this->getComment(Cell::stringFromColumnIndex($pColumn) . $pRow);
2349
    }
2350
2351
    /**
2352
     * Get selected cell
2353
     *
2354
     * @deprecated
2355
     * @return string
2356
     */
2357
    public function getSelectedCell()
2358
    {
2359
        return $this->getSelectedCells();
2360
    }
2361
2362
    /**
2363
     * Get active cell
2364
     *
2365
     * @return string Example: 'A1'
2366
     */
2367 57
    public function getActiveCell()
2368
    {
2369 57
        return $this->activeCell;
2370
    }
2371
2372
    /**
2373
     * Get selected cells
2374
     *
2375
     * @return string
2376
     */
2377 45
    public function getSelectedCells()
2378
    {
2379 45
        return $this->selectedCells;
2380
    }
2381
2382
    /**
2383
     * Selected cell
2384
     *
2385
     * @param    string        $pCoordinate    Cell (i.e. A1)
2386
     * @return Worksheet
2387
     */
2388
    public function setSelectedCell($pCoordinate = 'A1')
2389
    {
2390
        return $this->setSelectedCells($pCoordinate);
2391
    }
2392
2393
    /**
2394
     * Select a range of cells.
2395
     *
2396
     * @param    string        $pCoordinate    Cell range, examples: 'A1', 'B2:G5', 'A:C', '3:6'
2397
     * @throws    Exception
2398
     * @return Worksheet
2399
     */
2400 38
    public function setSelectedCells($pCoordinate = 'A1')
2401
    {
2402
        // Uppercase coordinate
2403 38
        $pCoordinate = strtoupper($pCoordinate);
2404
2405
        // Convert 'A' to 'A:A'
2406 38
        $pCoordinate = preg_replace('/^([A-Z]+)$/', '${1}:${1}', $pCoordinate);
2407
2408
        // Convert '1' to '1:1'
2409 38
        $pCoordinate = preg_replace('/^([0-9]+)$/', '${1}:${1}', $pCoordinate);
2410
2411
        // Convert 'A:C' to 'A1:C1048576'
2412 38
        $pCoordinate = preg_replace('/^([A-Z]+):([A-Z]+)$/', '${1}1:${2}1048576', $pCoordinate);
2413
2414
        // Convert '1:3' to 'A1:XFD3'
2415 38
        $pCoordinate = preg_replace('/^([0-9]+):([0-9]+)$/', 'A${1}:XFD${2}', $pCoordinate);
2416
2417 38
        if (strpos($pCoordinate, ':') !== false || strpos($pCoordinate, ',') !== false) {
2418 23
            list($first) = Cell::splitRange($pCoordinate);
2419 23
            $this->activeCell = $first[0];
2420
        } else {
2421 31
            $this->activeCell = $pCoordinate;
2422
        }
2423 38
        $this->selectedCells = $pCoordinate;
2424
2425 38
        return $this;
2426
    }
2427
2428
    /**
2429
     * Selected cell by using numeric cell coordinates
2430
     *
2431
     * @param int $pColumn Numeric column coordinate of the cell
2432
     * @param int $pRow Numeric row coordinate of the cell
2433
     * @throws Exception
2434
     * @return Worksheet
2435
     */
2436
    public function setSelectedCellByColumnAndRow($pColumn = 0, $pRow = 1)
2437
    {
2438
        return $this->setSelectedCells(Cell::stringFromColumnIndex($pColumn) . $pRow);
2439
    }
2440
2441
    /**
2442
     * Get right-to-left
2443
     *
2444
     * @return bool
2445
     */
2446 58
    public function getRightToLeft()
2447
    {
2448 58
        return $this->rightToLeft;
2449
    }
2450
2451
    /**
2452
     * Set right-to-left
2453
     *
2454
     * @param bool $value    Right-to-left true/false
2455
     * @return Worksheet
2456
     */
2457 4
    public function setRightToLeft($value = false)
2458
    {
2459 4
        $this->rightToLeft = $value;
2460
2461 4
        return $this;
2462
    }
2463
2464
    /**
2465
     * Fill worksheet from values in array
2466
     *
2467
     * @param array $source Source array
2468
     * @param mixed $nullValue Value in source array that stands for blank cell
2469
     * @param string $startCell Insert array starting from this cell address as the top left coordinate
2470
     * @param bool $strictNullComparison Apply strict comparison when testing for null values in the array
2471
     * @throws Exception
2472
     * @return Worksheet
2473
     */
2474 17
    public function fromArray($source = null, $nullValue = null, $startCell = 'A1', $strictNullComparison = false)
2475
    {
2476 17
        if (is_array($source)) {
2477
            //    Convert a 1-D array to 2-D (for ease of looping)
2478 17
            if (!is_array(end($source))) {
2479 3
                $source = [$source];
2480
            }
2481
2482
            // start coordinate
2483 17
            list($startColumn, $startRow) = Cell::coordinateFromString($startCell);
2484
2485
            // Loop through $source
2486 17
            foreach ($source as $rowData) {
2487 17
                $currentColumn = $startColumn;
2488 17
                foreach ($rowData as $cellValue) {
2489 17
                    if ($strictNullComparison) {
2490 1
                        if ($cellValue !== $nullValue) {
2491
                            // Set cell value
2492 1
                            $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2493
                        }
2494
                    } else {
2495 16
                        if ($cellValue != $nullValue) {
2496
                            // Set cell value
2497 16
                            $this->getCell($currentColumn . $startRow)->setValue($cellValue);
2498
                        }
2499
                    }
2500 17
                    ++$currentColumn;
2501
                }
2502 17
                ++$startRow;
2503
            }
2504
        } else {
2505
            throw new Exception('Parameter $source should be an array.');
2506
        }
2507
2508 17
        return $this;
2509
    }
2510
2511
    /**
2512
     * Create array from a range of cells
2513
     *
2514
     * @param string $pRange Range of cells (i.e. "A1:B10"), or just one cell (i.e. "A1")
2515
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2516
     * @param bool $calculateFormulas Should formulas be calculated?
2517
     * @param bool $formatData Should formatting be applied to cell values?
2518
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2519
     *                               True - Return rows and columns indexed by their actual row and column IDs
2520
     * @return array
2521
     */
2522 2
    public function rangeToArray($pRange = 'A1', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2523
    {
2524
        // Returnvalue
2525 2
        $returnValue = [];
2526
        //    Identify the range that we need to extract from the worksheet
2527 2
        list($rangeStart, $rangeEnd) = Cell::rangeBoundaries($pRange);
2528 2
        $minCol = Cell::stringFromColumnIndex($rangeStart[0] - 1);
2529 2
        $minRow = $rangeStart[1];
2530 2
        $maxCol = Cell::stringFromColumnIndex($rangeEnd[0] - 1);
2531 2
        $maxRow = $rangeEnd[1];
2532
2533 2
        ++$maxCol;
2534
        // Loop through rows
2535 2
        $r = -1;
2536 2
        for ($row = $minRow; $row <= $maxRow; ++$row) {
2537 2
            $rRef = ($returnCellRef) ? $row : ++$r;
2538 2
            $c = -1;
2539
            // Loop through columns in the current row
2540 2
            for ($col = $minCol; $col != $maxCol; ++$col) {
2541 2
                $cRef = ($returnCellRef) ? $col : ++$c;
2542
                //    Using getCell() will create a new cell if it doesn't already exist. We don't want that to happen
2543
                //        so we test and retrieve directly against cellCollection
2544 2
                if ($this->cellCollection->isDataSet($col . $row)) {
2545
                    // Cell exists
2546 2
                    $cell = $this->cellCollection->getCacheData($col . $row);
2547 2
                    if ($cell->getValue() !== null) {
2548 2
                        if ($cell->getValue() instanceof RichText) {
2549 2
                            $returnValue[$rRef][$cRef] = $cell->getValue()->getPlainText();
2550
                        } else {
2551 2
                            if ($calculateFormulas) {
2552 2
                                $returnValue[$rRef][$cRef] = $cell->getCalculatedValue();
2553
                            } else {
2554
                                $returnValue[$rRef][$cRef] = $cell->getValue();
2555
                            }
2556
                        }
2557
2558 2
                        if ($formatData) {
2559 2
                            $style = $this->parent->getCellXfByIndex($cell->getXfIndex());
2560 2
                            $returnValue[$rRef][$cRef] = Style\NumberFormat::toFormattedString(
2561 2
                                $returnValue[$rRef][$cRef],
2562 2
                                ($style && $style->getNumberFormat()) ? $style->getNumberFormat()->getFormatCode() : Style\NumberFormat::FORMAT_GENERAL
2563
                            );
2564
                        }
2565
                    } else {
2566
                        // Cell holds a NULL
2567 2
                        $returnValue[$rRef][$cRef] = $nullValue;
2568
                    }
2569
                } else {
2570
                    // Cell doesn't exist
2571 1
                    $returnValue[$rRef][$cRef] = $nullValue;
2572
                }
2573
            }
2574
        }
2575
2576
        // Return
2577 2
        return $returnValue;
2578
    }
2579
2580
    /**
2581
     * Create array from a range of cells
2582
     *
2583
     * @param  string $pNamedRange Name of the Named Range
2584
     * @param  mixed  $nullValue Value returned in the array entry if a cell doesn't exist
2585
     * @param  bool $calculateFormulas  Should formulas be calculated?
2586
     * @param  bool $formatData  Should formatting be applied to cell values?
2587
     * @param  bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2588
     *                                True - Return rows and columns indexed by their actual row and column IDs
2589
     * @throws Exception
2590
     * @return array
2591
     */
2592
    public function namedRangeToArray($pNamedRange = '', $nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2593
    {
2594
        $namedRange = NamedRange::resolveRange($pNamedRange, $this);
2595
        if ($namedRange !== null) {
2596
            $pWorkSheet = $namedRange->getWorksheet();
2597
            $pCellRange = $namedRange->getRange();
2598
2599
            return $pWorkSheet->rangeToArray($pCellRange, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
2600
        }
2601
2602
        throw new Exception('Named Range ' . $pNamedRange . ' does not exist.');
2603
    }
2604
2605
    /**
2606
     * Create array from worksheet
2607
     *
2608
     * @param mixed $nullValue Value returned in the array entry if a cell doesn't exist
2609
     * @param bool $calculateFormulas Should formulas be calculated?
2610
     * @param bool $formatData  Should formatting be applied to cell values?
2611
     * @param bool $returnCellRef False - Return a simple array of rows and columns indexed by number counting from zero
2612
     *                               True - Return rows and columns indexed by their actual row and column IDs
2613
     * @return array
2614
     */
2615
    public function toArray($nullValue = null, $calculateFormulas = true, $formatData = true, $returnCellRef = false)
2616
    {
2617
        // Garbage collect...
2618
        $this->garbageCollect();
2619
2620
        //    Identify the range that we need to extract from the worksheet
2621
        $maxCol = $this->getHighestColumn();
2622
        $maxRow = $this->getHighestRow();
2623
        // Return
2624
        return $this->rangeToArray('A1:' . $maxCol . $maxRow, $nullValue, $calculateFormulas, $formatData, $returnCellRef);
2625
    }
2626
2627
    /**
2628
     * Get row iterator
2629
     *
2630
     * @param   int   $startRow   The row number at which to start iterating
2631
     * @param   int   $endRow     The row number at which to stop iterating
2632
     *
2633
     * @return Worksheet\RowIterator
2634
     */
2635 2
    public function getRowIterator($startRow = 1, $endRow = null)
2636
    {
2637 2
        return new Worksheet\RowIterator($this, $startRow, $endRow);
2638
    }
2639
2640
    /**
2641
     * Get column iterator
2642
     *
2643
     * @param   string   $startColumn The column address at which to start iterating
2644
     * @param   string   $endColumn   The column address at which to stop iterating
2645
     *
2646
     * @return Worksheet\ColumnIterator
2647
     */
2648
    public function getColumnIterator($startColumn = 'A', $endColumn = null)
2649
    {
2650
        return new Worksheet\ColumnIterator($this, $startColumn, $endColumn);
2651
    }
2652
2653
    /**
2654
     * Run PhpSpreadsheet garabage collector.
2655
     *
2656
     * @return Worksheet
2657
     */
2658 59
    public function garbageCollect()
2659
    {
2660
        // Flush cache
2661 59
        $this->cellCollection->getCacheData('A1');
2662
2663
        // Lookup highest column and highest row if cells are cleaned
2664 59
        $colRow = $this->cellCollection->getHighestRowAndColumn();
2665 59
        $highestRow = $colRow['row'];
2666 59
        $highestColumn = Cell::columnIndexFromString($colRow['column']);
2667
2668
        // Loop through column dimensions
2669 59
        foreach ($this->columnDimensions as $dimension) {
2670 24
            $highestColumn = max($highestColumn, Cell::columnIndexFromString($dimension->getColumnIndex()));
2671
        }
2672
2673
        // Loop through row dimensions
2674 59
        foreach ($this->rowDimensions as $dimension) {
2675 39
            $highestRow = max($highestRow, $dimension->getRowIndex());
2676
        }
2677
2678
        // Cache values
2679 59
        if ($highestColumn < 0) {
2680
            $this->cachedHighestColumn = 'A';
2681
        } else {
2682 59
            $this->cachedHighestColumn = Cell::stringFromColumnIndex(--$highestColumn);
2683
        }
2684 59
        $this->cachedHighestRow = $highestRow;
2685
2686
        // Return
2687 59
        return $this;
2688
    }
2689
2690
    /**
2691
     * Get hash code
2692
     *
2693
     * @return string    Hash code
2694
     */
2695 60
    public function getHashCode()
2696
    {
2697 60
        if ($this->dirty) {
2698 60
            $this->hash = md5($this->title . $this->autoFilter . ($this->protection->isProtectionEnabled() ? 't' : 'f') . __CLASS__);
2699 60
            $this->dirty = false;
2700
        }
2701
2702 60
        return $this->hash;
2703
    }
2704
2705
    /**
2706
     * Extract worksheet title from range.
2707
     *
2708
     * Example: extractSheetTitle("testSheet!A1") ==> 'A1'
2709
     * Example: extractSheetTitle("'testSheet 1'!A1", true) ==> array('testSheet 1', 'A1');
2710
     *
2711
     * @param string $pRange    Range to extract title from
2712
     * @param bool $returnRange    Return range? (see example)
2713
     * @return mixed
2714
     */
2715 1
    public static function extractSheetTitle($pRange, $returnRange = false)
2716
    {
2717
        // Sheet title included?
2718 1
        if (($sep = strpos($pRange, '!')) === false) {
2719
            return '';
2720
        }
2721
2722 1
        if ($returnRange) {
2723 1
            return [trim(substr($pRange, 0, $sep), "'"), substr($pRange, $sep + 1)];
2724
        }
2725
2726
        return substr($pRange, $sep + 1);
2727
    }
2728
2729
    /**
2730
     * Get hyperlink
2731
     *
2732
     * @param string $pCellCoordinate    Cell coordinate to get hyperlink for
2733
     */
2734 11 View Code Duplication
    public function getHyperlink($pCellCoordinate = 'A1')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2735
    {
2736
        // return hyperlink if we already have one
2737 11
        if (isset($this->hyperlinkCollection[$pCellCoordinate])) {
2738 10
            return $this->hyperlinkCollection[$pCellCoordinate];
2739
        }
2740
2741
        // else create hyperlink
2742 11
        $this->hyperlinkCollection[$pCellCoordinate] = new Cell\Hyperlink();
2743
2744 11
        return $this->hyperlinkCollection[$pCellCoordinate];
2745
    }
2746
2747
    /**
2748
     * Set hyperlnk
2749
     *
2750
     * @param string $pCellCoordinate    Cell coordinate to insert hyperlink
2751
     * @param    Cell\Hyperlink    $pHyperlink
2752
     * @return Worksheet
2753
     */
2754 10
    public function setHyperlink($pCellCoordinate = 'A1', Cell\Hyperlink $pHyperlink = null)
2755
    {
2756 10
        if ($pHyperlink === null) {
2757 10
            unset($this->hyperlinkCollection[$pCellCoordinate]);
2758
        } else {
2759 10
            $this->hyperlinkCollection[$pCellCoordinate] = $pHyperlink;
2760
        }
2761
2762 10
        return $this;
2763
    }
2764
2765
    /**
2766
     * Hyperlink at a specific coordinate exists?
2767
     *
2768
     * @param string $pCoordinate
2769
     * @return bool
2770
     */
2771 3
    public function hyperlinkExists($pCoordinate = 'A1')
2772
    {
2773 3
        return isset($this->hyperlinkCollection[$pCoordinate]);
2774
    }
2775
2776
    /**
2777
     * Get collection of hyperlinks
2778
     *
2779
     * @return Cell\Hyperlink[]
2780
     */
2781 59
    public function getHyperlinkCollection()
2782
    {
2783 59
        return $this->hyperlinkCollection;
2784
    }
2785
2786
    /**
2787
     * Get data validation
2788
     *
2789
     * @param string $pCellCoordinate Cell coordinate to get data validation for
2790
     */
2791 2 View Code Duplication
    public function getDataValidation($pCellCoordinate = 'A1')
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

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

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

Loading history...
2792
    {
2793
        // return data validation if we already have one
2794 2
        if (isset($this->dataValidationCollection[$pCellCoordinate])) {
2795
            return $this->dataValidationCollection[$pCellCoordinate];
2796
        }
2797
2798
        // else create data validation
2799 2
        $this->dataValidationCollection[$pCellCoordinate] = new Cell\DataValidation();
2800
2801 2
        return $this->dataValidationCollection[$pCellCoordinate];
2802
    }
2803
2804
    /**
2805
     * Set data validation
2806
     *
2807
     * @param string $pCellCoordinate    Cell coordinate to insert data validation
2808
     * @param    Cell\DataValidation    $pDataValidation
2809
     * @return Worksheet
2810
     */
2811
    public function setDataValidation($pCellCoordinate = 'A1', Cell\DataValidation $pDataValidation = null)
2812
    {
2813
        if ($pDataValidation === null) {
2814
            unset($this->dataValidationCollection[$pCellCoordinate]);
2815
        } else {
2816
            $this->dataValidationCollection[$pCellCoordinate] = $pDataValidation;
2817
        }
2818
2819
        return $this;
2820
    }
2821
2822
    /**
2823
     * Data validation at a specific coordinate exists?
2824
     *
2825
     * @param string $pCoordinate
2826
     * @return bool
2827
     */
2828
    public function dataValidationExists($pCoordinate = 'A1')
2829
    {
2830
        return isset($this->dataValidationCollection[$pCoordinate]);
2831
    }
2832
2833
    /**
2834
     * Get collection of data validations
2835
     *
2836
     * @return Cell\DataValidation[]
2837
     */
2838 59
    public function getDataValidationCollection()
2839
    {
2840 59
        return $this->dataValidationCollection;
2841
    }
2842
2843
    /**
2844
     * Accepts a range, returning it as a range that falls within the current highest row and column of the worksheet
2845
     *
2846
     * @param string $range
2847
     * @return string Adjusted range value
2848
     */
2849
    public function shrinkRangeToFit($range)
2850
    {
2851
        $maxCol = $this->getHighestColumn();
2852
        $maxRow = $this->getHighestRow();
2853
        $maxCol = Cell::columnIndexFromString($maxCol);
2854
2855
        $rangeBlocks = explode(' ', $range);
2856
        foreach ($rangeBlocks as &$rangeSet) {
2857
            $rangeBoundaries = Cell::getRangeBoundaries($rangeSet);
2858
2859 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...
2860
                $rangeBoundaries[0][0] = Cell::stringFromColumnIndex($maxCol);
2861
            }
2862
            if ($rangeBoundaries[0][1] > $maxRow) {
2863
                $rangeBoundaries[0][1] = $maxRow;
2864
            }
2865 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...
2866
                $rangeBoundaries[1][0] = Cell::stringFromColumnIndex($maxCol);
2867
            }
2868
            if ($rangeBoundaries[1][1] > $maxRow) {
2869
                $rangeBoundaries[1][1] = $maxRow;
2870
            }
2871
            $rangeSet = $rangeBoundaries[0][0] . $rangeBoundaries[0][1] . ':' . $rangeBoundaries[1][0] . $rangeBoundaries[1][1];
2872
        }
2873
        unset($rangeSet);
2874
        $stRange = implode(' ', $rangeBlocks);
2875
2876
        return $stRange;
2877
    }
2878
2879
    /**
2880
     * Get tab color
2881
     *
2882
     * @return Style\Color
2883
     */
2884 9
    public function getTabColor()
2885
    {
2886 9
        if ($this->tabColor === null) {
2887 9
            $this->tabColor = new Style\Color();
2888
        }
2889
2890 9
        return $this->tabColor;
2891
    }
2892
2893
    /**
2894
     * Reset tab color
2895
     *
2896
     * @return Worksheet
2897
     */
2898
    public function resetTabColor()
2899
    {
2900
        $this->tabColor = null;
2901
        unset($this->tabColor);
2902
2903
        return $this;
2904
    }
2905
2906
    /**
2907
     * Tab color set?
2908
     *
2909
     * @return bool
2910
     */
2911 58
    public function isTabColorSet()
2912
    {
2913 58
        return $this->tabColor !== null;
2914
    }
2915
2916
    /**
2917
     * Copy worksheet (!= clone!)
2918
     *
2919
     * @return Worksheet
2920
     */
2921
    public function copy()
2922
    {
2923
        $copied = clone $this;
2924
2925
        return $copied;
2926
    }
2927
2928
    /**
2929
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
2930
     */
2931 1
    public function __clone()
2932
    {
2933 1
        foreach ($this as $key => $val) {
0 ignored issues
show
Bug introduced by
The expression $this of type this<PhpOffice\PhpSpreadsheet\Worksheet> is not traversable.
Loading history...
2934 1
            if ($key == 'parent') {
2935 1
                continue;
2936
            }
2937
2938 1
            if (is_object($val) || (is_array($val))) {
2939 1
                if ($key == 'cellCollection') {
2940 1
                    $newCollection = clone $this->cellCollection;
2941 1
                    $newCollection->copyCellCollection($this);
2942 1
                    $this->cellCollection = $newCollection;
2943 1
                } elseif ($key == 'drawingCollection') {
2944 1
                    $newCollection = clone $this->drawingCollection;
2945 1
                    $this->drawingCollection = $newCollection;
0 ignored issues
show
Documentation Bug introduced by
It seems like $newCollection can also be of type object. However, the property $drawingCollection is declared as type array<integer,object<Php...Worksheet\BaseDrawing>>. Maybe add an additional type check?

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

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

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

class Id
{
    public $id;

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

}

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

$account_id = false;

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

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
2946 1
                } elseif (($key == 'autoFilter') && ($this->autoFilter instanceof Worksheet\AutoFilter)) {
2947 1
                    $newAutoFilter = clone $this->autoFilter;
2948 1
                    $this->autoFilter = $newAutoFilter;
2949 1
                    $this->autoFilter->setParent($this);
2950
                } else {
2951 1
                    $this->{$key} = unserialize(serialize($val));
2952
                }
2953
            }
2954
        }
2955 1
    }
2956
    /**
2957
     * Define the code name of the sheet
2958
     *
2959
     * @param null|string Same rule as Title minus space not allowed (but, like Excel, change silently space to underscore)
2960
     * @throws Exception
2961
     * @return objWorksheet
2962
     */
2963 70
    public function setCodeName($pValue = null)
2964
    {
2965
        // Is this a 'rename' or not?
2966 70
        if ($this->getCodeName() == $pValue) {
2967
            return $this;
2968
        }
2969 70
        $pValue = str_replace(' ', '_', $pValue); //Excel does this automatically without flinching, we are doing the same
2970
        // Syntax check
2971
        // throw an exception if not valid
2972 70
        self::checkSheetCodeName($pValue);
2973
2974
        // We use the same code that setTitle to find a valid codeName else not using a space (Excel don't like) but a '_'
2975
2976 70
        if ($this->getParent()) {
2977
            // Is there already such sheet name?
2978 62
            if ($this->getParent()->sheetCodeNameExists($pValue)) {
2979
                // Use name, but append with lowest possible integer
2980
2981 17
                if (Shared\StringHelper::countCharacters($pValue) > 29) {
2982
                    $pValue = Shared\StringHelper::substring($pValue, 0, 29);
2983
                }
2984 17
                $i = 1;
2985 17 View Code Duplication
                while ($this->getParent()->sheetCodeNameExists($pValue . '_' . $i)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

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

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

Loading history...
2986 3
                    ++$i;
2987 3
                    if ($i == 10) {
2988
                        if (Shared\StringHelper::countCharacters($pValue) > 28) {
2989
                            $pValue = Shared\StringHelper::substring($pValue, 0, 28);
2990
                        }
2991 3
                    } elseif ($i == 100) {
2992
                        if (Shared\StringHelper::countCharacters($pValue) > 27) {
2993
                            $pValue = Shared\StringHelper::substring($pValue, 0, 27);
2994
                        }
2995
                    }
2996
                }
2997
2998 17
                $pValue = $pValue . '_' . $i; // ok, we have a valid name
2999
                //codeName is'nt used in formula : no need to call for an update
3000
                //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...
3001
            }
3002
        }
3003
3004 70
        $this->codeName = $pValue;
3005
3006 70
        return $this;
3007
    }
3008
    /**
3009
     * Return the code name of the sheet
3010
     *
3011
     * @return null|string
3012
     */
3013 70
    public function getCodeName()
3014
    {
3015 70
        return $this->codeName;
3016
    }
3017
    /**
3018
     * Sheet has a code name ?
3019
     * @return bool
3020
     */
3021
    public function hasCodeName()
3022
    {
3023
        return !(is_null($this->codeName));
3024
    }
3025
}
3026