Completed
Push — develop ( e95e4d...4fd8e7 )
by Adrien
61:55
created

Cell::__clone()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 7
nc 3
nop 0
dl 0
loc 11
ccs 3
cts 3
cp 1
crap 4
rs 9.2
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet;
4
5
use PhpOffice\PhpSpreadsheet\Collection\Cells;
6
7
class Cell
8
{
9
    /**
10
     * Default range variable constant.
11
     *
12
     * @var string
13
     */
14
    const DEFAULT_RANGE = 'A1:A1';
15
16
    /**
17
     * Value binder to use.
18
     *
19
     * @var Cell\IValueBinder
20
     */
21
    private static $valueBinder;
22
23
    /**
24
     * Value of the cell.
25
     *
26
     * @var mixed
27
     */
28
    private $value;
29
30
    /**
31
     *    Calculated value of the cell (used for caching)
32
     *    This returns the value last calculated by MS Excel or whichever spreadsheet program was used to
33
     *        create the original spreadsheet file.
34
     *    Note that this value is not guaranteed to reflect the actual calculated value because it is
35
     *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data
36
     *        values used by the formula have changed since it was last calculated.
37
     *
38
     * @var mixed
39
     */
40
    private $calculatedValue;
41
42
    /**
43
     * Type of the cell data.
44
     *
45
     * @var string
46
     */
47
    private $dataType;
48
49
    /**
50
     * Collection of cells.
51
     *
52
     * @var Cells
53
     */
54
    private $parent;
55
56
    /**
57
     * Index to cellXf.
58
     *
59
     * @var int
60
     */
61
    private $xfIndex = 0;
62
63
    /**
64
     * Attributes of the formula.
65
     */
66
    private $formulaAttributes;
67
68
    /**
69
     * Update the cell into the cell collection.
70
     *
71
     * @return self
72
     */
73 119
    public function updateInCollection()
74
    {
75 119
        $this->parent->update($this);
76
77 119
        return $this;
78
    }
79
80 109
    public function detach()
81
    {
82 109
        $this->parent = null;
83 109
    }
84
85 101
    public function attach(Cells $parent)
86
    {
87 101
        $this->parent = $parent;
88 101
    }
89
90
    /**
91
     * Create a new Cell.
92
     *
93
     * @param mixed $pValue
94
     * @param string $pDataType
95
     * @param Worksheet $pSheet
96
     *
97
     * @throws Exception
98
     */
99 121
    public function __construct($pValue, $pDataType, Worksheet $pSheet)
100
    {
101
        // Initialise cell value
102 121
        $this->value = $pValue;
103
104
        // Set worksheet cache
105 121
        $this->parent = $pSheet->getCellCollection();
106
107
        // Set datatype?
108 121
        if ($pDataType !== null) {
109 121
            if ($pDataType == Cell\DataType::TYPE_STRING2) {
110
                $pDataType = Cell\DataType::TYPE_STRING;
111
            }
112 121
            $this->dataType = $pDataType;
113
        } elseif (!self::getValueBinder()->bindValue($this, $pValue)) {
114
            throw new Exception('Value could not be bound to cell.');
115
        }
116 121
    }
117
118
    /**
119
     * Get cell coordinate column.
120
     *
121
     * @return string
122
     */
123 48
    public function getColumn()
124
    {
125 48
        return $this->parent->getCurrentColumn();
126
    }
127
128
    /**
129
     * Get cell coordinate row.
130
     *
131
     * @return int
132
     */
133 46
    public function getRow()
134
    {
135 46
        return $this->parent->getCurrentRow();
136
    }
137
138
    /**
139
     * Get cell coordinate.
140
     *
141
     * @return string
142
     */
143 121
    public function getCoordinate()
144
    {
145 121
        return $this->parent->getCurrentCoordinate();
146
    }
147
148
    /**
149
     * Get cell value.
150
     *
151
     * @return mixed
152
     */
153 106
    public function getValue()
154
    {
155 106
        return $this->value;
156
    }
157
158
    /**
159
     * Get cell value with formatting.
160
     *
161
     * @return string
162
     */
163 5
    public function getFormattedValue()
164
    {
165 5
        return (string) Style\NumberFormat::toFormattedString(
166 5
            $this->getCalculatedValue(),
167 5
            $this->getStyle()
168 5
                ->getNumberFormat()->getFormatCode()
169
        );
170
    }
171
172
    /**
173
     * Set cell value.
174
     *
175
     *    Sets the value for a cell, automatically determining the datatype using the value binder
176
     *
177
     * @param mixed $pValue Value
178
     *
179
     * @throws Exception
180
     *
181
     * @return Cell
182
     */
183 77
    public function setValue($pValue)
184
    {
185 77
        if (!self::getValueBinder()->bindValue($this, $pValue)) {
186
            throw new Exception('Value could not be bound to cell.');
187
        }
188
189 77
        return $this;
190
    }
191
192
    /**
193
     * Set the value for a cell, with the explicit data type passed to the method (bypassing any use of the value binder).
194
     *
195
     * @param mixed $pValue Value
196
     * @param string $pDataType Explicit data type, see Cell\DataType::TYPE_*
197
     *
198
     * @throws Exception
199
     *
200
     * @return Cell
201
     */
202 115
    public function setValueExplicit($pValue, $pDataType)
203
    {
204
        // set the value according to data type
205
        switch ($pDataType) {
206 115
            case Cell\DataType::TYPE_NULL:
207 28
                $this->value = $pValue;
208 28
209 115
                break;
210
            case Cell\DataType::TYPE_STRING2:
211
                $pDataType = Cell\DataType::TYPE_STRING;
212 115
                // no break
213
            case Cell\DataType::TYPE_STRING:
214 98
                // Synonym for string
215
            case Cell\DataType::TYPE_INLINE:
216 99
                // Rich text
217 99
                $this->value = Cell\DataType::checkString($pValue);
218 97
219 91
                break;
220 91
            case Cell\DataType::TYPE_NUMERIC:
221 58
                $this->value = (float) $pValue;
222 56
223 56
                break;
224 25
            case Cell\DataType::TYPE_FORMULA:
225 25
                $this->value = (string) $pValue;
226 25
227
                break;
228
            case Cell\DataType::TYPE_BOOL:
229
                $this->value = (bool) $pValue;
230
231
                break;
232
            case Cell\DataType::TYPE_ERROR:
233
                $this->value = Cell\DataType::checkErrorCode($pValue);
234
235
                break;
236 115
            default:
237
                throw new Exception('Invalid datatype: ' . $pDataType);
238 115
                break;
0 ignored issues
show
Unused Code introduced by
break; does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
239
        }
240
241
        // set the datatype
242
        $this->dataType = $pDataType;
243
244
        return $this->updateInCollection();
245
    }
246
247
    /**
248
     * Get calculated cell value.
249
     *
250 69
     * @param bool $resetLog Whether the calculation engine logger should be reset or not
251
     *
252 69
     * @throws Exception
253
     *
254 47
     * @return mixed
255 47
     */
256 47
    public function getCalculatedValue($resetLog = true)
257
    {
258 47
        if ($this->dataType == Cell\DataType::TYPE_FORMULA) {
259 47
            try {
260 10
                $result = Calculation::getInstance(
261
                    $this->getWorksheet()->getParent()
262
                )->calculateCellValue($this, $resetLog);
263
                //    We don't yet handle array returns
264
                if (is_array($result)) {
265
                    while (is_array($result)) {
266
                        $result = array_pop($result);
267
                    }
268
                }
269
            } catch (Exception $ex) {
270
                if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) {
271
                    return $this->calculatedValue; // Fallback for calculations referencing external files.
272
                }
273 47
                $result = '#N/A';
0 ignored issues
show
Unused Code introduced by
$result 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...
274
275
                throw new Calculation\Exception(
276
                    $this->getWorksheet()->getTitle() . '!' . $this->getCoordinate() . ' -> ' . $ex->getMessage()
277 47
                );
278 69
            }
279 2
280
            if ($result === '#Not Yet Implemented') {
281
                return $this->calculatedValue; // Fallback if calculation engine does not support the formula.
282 69
            }
283
284
            return $result;
285
        } elseif ($this->value instanceof RichText) {
286
            return $this->value->getPlainText();
287
        }
288
289
        return $this->value;
290
    }
291
292 21
    /**
293
     * Set old calculated value (cached).
294 21
     *
295 21
     * @param mixed $pValue Value
296
     *
297
     * @return Cell
298 21
     */
299
    public function setCalculatedValue($pValue)
300
    {
301
        if ($pValue !== null) {
302
            $this->calculatedValue = (is_numeric($pValue)) ? (float) $pValue : $pValue;
303
        }
304
305
        return $this->updateInCollection();
306
    }
307
308
    /**
309
     *    Get old calculated value (cached)
310
     *    This returns the value last calculated by MS Excel or whichever spreadsheet program was used to
311
     *        create the original spreadsheet file.
312
     *    Note that this value is not guaranteed to refelect the actual calculated value because it is
313
     *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data
314
     *        values used by the formula have changed since it was last calculated.
315
     *
316
     * @return mixed
317
     */
318
    public function getOldCalculatedValue()
319
    {
320
        return $this->calculatedValue;
321 70
    }
322
323 70
    /**
324
     * Get cell data type.
325
     *
326
     * @return string
327
     */
328
    public function getDataType()
329
    {
330
        return $this->dataType;
331
    }
332
333
    /**
334
     * Set cell data type.
335
     *
336
     * @param string $pDataType see Cell\DataType::TYPE_*
337
     *
338
     * @return Cell
339
     */
340
    public function setDataType($pDataType)
341
    {
342
        if ($pDataType == Cell\DataType::TYPE_STRING2) {
343
            $pDataType = Cell\DataType::TYPE_STRING;
344
        }
345
        $this->dataType = $pDataType;
346
347
        return $this->updateInCollection();
348
    }
349
350
    /**
351
     * Identify if the cell contains a formula.
352
     *
353
     * @return bool
354
     */
355
    public function isFormula()
356
    {
357
        return $this->dataType == Cell\DataType::TYPE_FORMULA;
358
    }
359
360
    /**
361
     *    Does this cell contain Data validation rules?
362
     *
363
     * @throws Exception
364
     *
365
     * @return bool
366
     */
367
    public function hasDataValidation()
368
    {
369
        if (!isset($this->parent)) {
370
            throw new Exception('Cannot check for data validation when cell is not bound to a worksheet');
371
        }
372
373
        return $this->getWorksheet()->dataValidationExists($this->getCoordinate());
374
    }
375
376 2
    /**
377
     * Get Data validation rules.
378 2
     *
379
     * @throws Exception
380
     *
381
     * @return Cell\DataValidation
382 2
     */
383
    public function getDataValidation()
384
    {
385
        if (!isset($this->parent)) {
386
            throw new Exception('Cannot get data validation for cell that is not bound to a worksheet');
387
        }
388
389
        return $this->getWorksheet()->getDataValidation($this->getCoordinate());
390
    }
391
392
    /**
393
     * Set Data validation rules.
394
     *
395
     * @param Cell\DataValidation $pDataValidation
396
     *
397
     * @throws Exception
398
     *
399
     * @return Cell
400
     */
401
    public function setDataValidation(Cell\DataValidation $pDataValidation = null)
402
    {
403
        if (!isset($this->parent)) {
404
            throw new Exception('Cannot set data validation for cell that is not bound to a worksheet');
405
        }
406
407
        $this->getWorksheet()->setDataValidation($this->getCoordinate(), $pDataValidation);
408
409
        return $this->updateInCollection();
410
    }
411
412
    /**
413
     *    Does this cell contain a Hyperlink?
414
     *
415
     * @throws Exception
416
     *
417
     * @return bool
418
     */
419
    public function hasHyperlink()
420
    {
421
        if (!isset($this->parent)) {
422
            throw new Exception('Cannot check for hyperlink when cell is not bound to a worksheet');
423
        }
424
425
        return $this->getWorksheet()->hyperlinkExists($this->getCoordinate());
426
    }
427
428 21
    /**
429
     * Get Hyperlink.
430 21
     *
431
     * @throws Exception
432
     *
433
     * @return Cell\Hyperlink
434 21
     */
435
    public function getHyperlink()
436
    {
437
        if (!isset($this->parent)) {
438
            throw new Exception('Cannot get hyperlink for cell that is not bound to a worksheet');
439
        }
440
441
        return $this->getWorksheet()->getHyperlink($this->getCoordinate());
442
    }
443
444
    /**
445
     * Set Hyperlink.
446
     *
447
     * @param Cell\Hyperlink $pHyperlink
448
     *
449
     * @throws Exception
450
     *
451
     * @return Cell
452
     */
453
    public function setHyperlink(Cell\Hyperlink $pHyperlink = null)
454
    {
455
        if (!isset($this->parent)) {
456
            throw new Exception('Cannot set hyperlink for cell that is not bound to a worksheet');
457
        }
458
459
        $this->getWorksheet()->setHyperlink($this->getCoordinate(), $pHyperlink);
460
461
        return $this->updateInCollection();
462 62
    }
463
464 62
    /**
465
     * Get cell collection.
466
     *
467
     * @return Cells
468
     */
469
    public function getParent()
470
    {
471
        return $this->parent;
472 76
    }
473
474 76
    /**
475
     * Get parent worksheet.
476
     *
477
     * @return Worksheet
478
     */
479
    public function getWorksheet()
480
    {
481
        return $this->parent->getParent();
482
    }
483
484
    /**
485
     * Is this cell in a merge range.
486
     *
487
     * @return bool
488
     */
489
    public function isInMergeRange()
490
    {
491
        return (bool) $this->getMergeRange();
492
    }
493
494
    /**
495
     * Is this cell the master (top left cell) in a merge range (that holds the actual data value).
496
     *
497
     * @return bool
498
     */
499
    public function isMergeRangeValueCell()
500
    {
501
        if ($mergeRange = $this->getMergeRange()) {
502
            $mergeRange = self::splitRange($mergeRange);
0 ignored issues
show
Documentation introduced by
$mergeRange 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...
503
            [$startCell] = $mergeRange[0];
0 ignored issues
show
Bug introduced by
The variable $startCell does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
504
            if ($this->getCoordinate() === $startCell) {
505
                return true;
506
            }
507
        }
508
509
        return false;
510
    }
511
512
    /**
513
     * If this cell is in a merge range, then return the range.
514
     *
515
     * @return string
516
     */
517
    public function getMergeRange()
518
    {
519
        foreach ($this->getWorksheet()->getMergeCells() as $mergeRange) {
520
            if ($this->isInRange($mergeRange)) {
0 ignored issues
show
Documentation introduced by
$mergeRange 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...
521
                return $mergeRange;
522
            }
523
        }
524
525
        return false;
526 6
    }
527
528 6
    /**
529
     * Get cell style.
530
     *
531
     * @return Style
532
     */
533
    public function getStyle()
534
    {
535
        return $this->getWorksheet()->getStyle($this->getCoordinate());
536
    }
537
538
    /**
539
     * Re-bind parent.
540
     *
541
     * @param Worksheet $parent
542
     *
543
     * @return Cell
544
     */
545
    public function rebindParent(Worksheet $parent)
546
    {
547
        $this->parent = $parent->getCellCollection();
548
549
        return $this->updateInCollection();
550
    }
551
552
    /**
553
     *    Is cell in a specific range?
554
     *
555
     * @param string $pRange Cell range (e.g. A1:A1)
556
     *
557
     * @return bool
558
     */
559
    public function isInRange($pRange)
560
    {
561
        [$rangeStart, $rangeEnd] = self::rangeBoundaries($pRange);
0 ignored issues
show
Bug introduced by
The variable $rangeStart does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $rangeEnd does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
562
563
        // Translate properties
564
        $myColumn = self::columnIndexFromString($this->getColumn());
565
        $myRow = $this->getRow();
566
567
        // Verify if cell is in range
568
        return ($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn) &&
569
                ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow);
570
    }
571
572
    /**
573
     * Coordinate from string.
574 170
     *
575
     * @param string $pCoordinateString eg: 'A1'
576 170
     *
577 167
     * @throws Exception
578 3
     *
579 1
     * @return string[] Array containing column and row (indexes 0 and 1)
580 2
     */
581 1
    public static function coordinateFromString($pCoordinateString)
582
    {
583
        if (preg_match("/^([$]?[A-Z]{1,3})([$]?\d{1,7})$/", $pCoordinateString, $matches)) {
584 1
            return [$matches[1], $matches[2]];
585
        } elseif ((strpos($pCoordinateString, ':') !== false) || (strpos($pCoordinateString, ',') !== false)) {
586
            throw new Exception('Cell coordinate string can not be a range of cells');
587
        } elseif ($pCoordinateString == '') {
588
            throw new Exception('Cell coordinate can not be zero-length string');
589
        }
590
591
        throw new Exception('Invalid cell coordinate ' . $pCoordinateString);
592
    }
593
594
    /**
595
     * Make string row, column or cell coordinate absolute.
596
     *
597 21
     * @param string $pCoordinateString e.g. 'A' or '1' or 'A1'
598
     *                    Note that this value can be a row or column reference as well as a cell reference
599 21
     *
600
     * @throws Exception
601 20
     *
602 20
     * @return string Absolute coordinate        e.g. '$A' or '$1' or '$A$1'
603 20
     */
604 8
    public static function absoluteReference($pCoordinateString)
605
    {
606 20
        if (strpos($pCoordinateString, ':') === false && strpos($pCoordinateString, ',') === false) {
607 8
            // Split out any worksheet name from the reference
608
            $worksheet = '';
609
            $cellAddress = explode('!', $pCoordinateString);
610
            if (count($cellAddress) > 1) {
611 20
                [$worksheet, $pCoordinateString] = $cellAddress;
612 2
            }
613 18
            if ($worksheet > '') {
614 2
                $worksheet .= '!';
615
            }
616
617 16
            // Create absolute coordinate
618
            if (ctype_digit($pCoordinateString)) {
619
                return $worksheet . '$' . $pCoordinateString;
620 1
            } elseif (ctype_alpha($pCoordinateString)) {
621
                return $worksheet . '$' . strtoupper($pCoordinateString);
622
            }
623
624
            return $worksheet . self::absoluteCoordinate($pCoordinateString);
625
        }
626
627
        throw new Exception('Cell coordinate string can not be a range of cells');
628
    }
629
630
    /**
631
     * Make string coordinate absolute.
632 32
     *
633
     * @param string $pCoordinateString e.g. 'A1'
634 32
     *
635
     * @throws Exception
636 31
     *
637 31
     * @return string Absolute coordinate        e.g. '$A$1'
638 31
     */
639 6
    public static function absoluteCoordinate($pCoordinateString)
640
    {
641 31
        if (strpos($pCoordinateString, ':') === false && strpos($pCoordinateString, ',') === false) {
642 6
            // Split out any worksheet name from the coordinate
643
            $worksheet = '';
644
            $cellAddress = explode('!', $pCoordinateString);
645
            if (count($cellAddress) > 1) {
646 31
                [$worksheet, $pCoordinateString] = $cellAddress;
647 31
            }
648 31
            if ($worksheet > '') {
649
                $worksheet .= '!';
650 31
            }
651
652
            // Create absolute coordinate
653 1
            [$column, $row] = self::coordinateFromString($pCoordinateString);
0 ignored issues
show
Bug introduced by
The variable $column seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $row seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
654
            $column = ltrim($column, '$');
0 ignored issues
show
Bug introduced by
The variable $column seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
655
            $row = ltrim($row, '$');
0 ignored issues
show
Bug introduced by
The variable $row seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
656
657
            return $worksheet . '$' . $column . '$' . $row;
658
        }
659
660
        throw new Exception('Cell coordinate string can not be a range of cells');
661
    }
662
663
    /**
664
     * Split range into coordinate strings.
665 102
     *
666
     * @param string $pRange e.g. 'B4:D9' or 'B4:D9,H2:O11' or 'B4'
667
     *
668 102
     * @return array Array containg one or more arrays containing one or two coordinate strings
669
     *                                e.g. array('B4','D9') or array(array('B4','D9'),array('H2','O11'))
670
     *                                        or array('B4')
671
     */
672 102
    public static function splitRange($pRange)
673 102
    {
674 102
        // Ensure $pRange is a valid range
675 102
        if (empty($pRange)) {
676
            $pRange = self::DEFAULT_RANGE;
677
        }
678 102
679
        $exploded = explode(',', $pRange);
680
        $counter = count($exploded);
681
        for ($i = 0; $i < $counter; ++$i) {
682
            $exploded[$i] = explode(':', $exploded[$i]);
683
        }
684
685
        return $exploded;
686
    }
687
688
    /**
689
     * Build range from coordinate strings.
690 19
     *
691
     * @param array $pRange Array containg one or more arrays containing one or two coordinate strings
692
     *
693 19
     * @throws Exception
694
     *
695
     * @return string String representation of $pRange
696
     */
697
    public static function buildRange(array $pRange)
698 19
    {
699 19
        // Verify range
700 19
        if (empty($pRange) || !is_array($pRange[0])) {
701 19
            throw new Exception('Range does not contain any information');
702
        }
703 19
704
        // Build range
705 19
        $imploded = [];
0 ignored issues
show
Unused Code introduced by
$imploded 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...
706
        $counter = count($pRange);
707
        for ($i = 0; $i < $counter; ++$i) {
708
            $pRange[$i] = implode(':', $pRange[$i]);
709
        }
710
        $imploded = implode(',', $pRange);
711
712
        return $imploded;
713
    }
714
715
    /**
716 64
     * Calculate range boundaries.
717
     *
718
     * @param string $pRange Cell range (e.g. A1:A1)
719 64
     *
720
     * @return array Range coordinates array(Start Cell, End Cell)
721
     *                    where Start Cell and End Cell are arrays (Column Number, Row Number)
722
     */
723
    public static function rangeBoundaries($pRange)
724 64
    {
725
        // Ensure $pRange is a valid range
726
        if (empty($pRange)) {
727 64
            $pRange = self::DEFAULT_RANGE;
728 2
        }
729
730 62
        // Uppercase coordinate
731
        $pRange = strtoupper($pRange);
732
733
        // Extract range
734 64
        if (strpos($pRange, ':') === false) {
735 64
            $rangeA = $rangeB = $pRange;
736
        } else {
737
            [$rangeA, $rangeB] = explode(':', $pRange);
0 ignored issues
show
Bug introduced by
The variable $rangeA seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $rangeB seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
738 64
        }
739 64
740
        // Calculate range outer borders
741 64
        $rangeStart = self::coordinateFromString($rangeA);
0 ignored issues
show
Bug introduced by
The variable $rangeA does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
742
        $rangeEnd = self::coordinateFromString($rangeB);
0 ignored issues
show
Bug introduced by
The variable $rangeB does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
743
744
        // Translate column into index
745
        $rangeStart[0] = self::columnIndexFromString($rangeStart[0]);
746
        $rangeEnd[0] = self::columnIndexFromString($rangeEnd[0]);
747
748
        return [$rangeStart, $rangeEnd];
749
    }
750
751 15
    /**
752
     * Calculate range dimension.
753
     *
754 15
     * @param string $pRange Cell range (e.g. A1:A1)
755
     *
756 15
     * @return array Range dimension (width, height)
757
     */
758
    public static function rangeDimension($pRange)
759
    {
760
        // Calculate range outer borders
761
        [$rangeStart, $rangeEnd] = self::rangeBoundaries($pRange);
0 ignored issues
show
Bug introduced by
The variable $rangeStart does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $rangeEnd does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
762
763
        return [($rangeEnd[0] - $rangeStart[0] + 1), ($rangeEnd[1] - $rangeStart[1] + 1)];
764
    }
765
766
    /**
767 13
     * Calculate range boundaries.
768
     *
769
     * @param string $pRange Cell range (e.g. A1:A1)
770 13
     *
771
     * @return array Range coordinates array(Start Cell, End Cell)
772
     *                    where Start Cell and End Cell are arrays (Column ID, Row Number)
773
     */
774
    public static function getRangeBoundaries($pRange)
775 13
    {
776
        // Ensure $pRange is a valid range
777
        if (empty($pRange)) {
778 13
            $pRange = self::DEFAULT_RANGE;
779 1
        }
780
781 12
        // Uppercase coordinate
782
        $pRange = strtoupper($pRange);
783
784 13
        // Extract range
785
        if (strpos($pRange, ':') === false) {
786
            $rangeA = $rangeB = $pRange;
787
        } else {
788
            [$rangeA, $rangeB] = explode(':', $pRange);
0 ignored issues
show
Bug introduced by
The variable $rangeA seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $rangeB seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
789
        }
790
791
        return [self::coordinateFromString($rangeA), self::coordinateFromString($rangeB)];
0 ignored issues
show
Bug introduced by
The variable $rangeA does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $rangeB does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
792
    }
793
794 162
    /**
795
     * Column index from string.
796
     *
797
     * @param string $pString eg 'A'
798
     *
799 162
     * @return int Column index (base 1 !!!)
800
     */
801 162
    public static function columnIndexFromString($pString)
802 147
    {
803
        //    Using a lookup cache adds a slight memory overhead, but boosts speed
804
        //    caching using a static within the method is faster than a class static,
805
        //        though it's additional memory overhead
806
        static $_indexCache = [];
807 117
808
        if (isset($_indexCache[$pString])) {
809
            return $_indexCache[$pString];
810
        }
811
        //    It's surprising how costly the strtoupper() and ord() calls actually are, so we use a lookup array rather than use ord()
812
        //        and make it case insensitive to get rid of the strtoupper() as well. Because it's a static, there's no significant
813
        //        memory overhead either
814
        static $_columnLookup = [
815
            'A' => 1, 'B' => 2, 'C' => 3, 'D' => 4, 'E' => 5, 'F' => 6, 'G' => 7, 'H' => 8, 'I' => 9, 'J' => 10, 'K' => 11, 'L' => 12, 'M' => 13,
816 117
            'N' => 14, 'O' => 15, 'P' => 16, 'Q' => 17, 'R' => 18, 'S' => 19, 'T' => 20, 'U' => 21, 'V' => 22, 'W' => 23, 'X' => 24, 'Y' => 25, 'Z' => 26,
817 115
            'a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6, 'g' => 7, 'h' => 8, 'i' => 9, 'j' => 10, 'k' => 11, 'l' => 12, 'm' => 13,
818 104
            'n' => 14, 'o' => 15, 'p' => 16, 'q' => 17, 'r' => 18, 's' => 19, 't' => 20, 'u' => 21, 'v' => 22, 'w' => 23, 'x' => 24, 'y' => 25, 'z' => 26,
819
        ];
820 104
821 12
        //    We also use the language construct isset() rather than the more costly strlen() function to match the length of $pString
822 9
        //        for improved performance
823
        if (isset($pString[0])) {
824 9
            if (!isset($pString[1])) {
825 3
                $_indexCache[$pString] = $_columnLookup[$pString];
826 2
827
                return $_indexCache[$pString];
828 2
            } elseif (!isset($pString[2])) {
829
                $_indexCache[$pString] = $_columnLookup[$pString[0]] * 26 + $_columnLookup[$pString[1]];
830
831 3
                return $_indexCache[$pString];
832
            } elseif (!isset($pString[3])) {
833
                $_indexCache[$pString] = $_columnLookup[$pString[0]] * 676 + $_columnLookup[$pString[1]] * 26 + $_columnLookup[$pString[2]];
834
835
                return $_indexCache[$pString];
836
            }
837
        }
838
839
        throw new Exception('Column string index can not be ' . ((isset($pString[0])) ? 'longer than 3 characters' : 'empty'));
840
    }
841 131
842
    /**
843 131
     * String from columnindex.
844
     *
845 131
     * @param int $columnIndex Column index (A = 0)
846 112
     *
847 112
     * @return string
848
     */
849 112
    public static function stringFromColumnIndex($columnIndex)
850 112
    {
851 112
        static $indexCache = [];
852 112
853 112
        if (!isset($indexCache[$columnIndex])) {
854
            $indexValue = $columnIndex + 1;
855
            $base26 = null;
856 131
            do {
857
                $characterValue = ($indexValue % 26) ?: 26;
858
                $indexValue = ($indexValue - $characterValue) / 26;
859
                $base26 = chr($characterValue + 64) . ($base26 ?: '');
860
            } while ($indexValue > 0);
861
            $indexCache[$columnIndex] = $base26;
862
        }
863
864
        return $indexCache[$columnIndex];
865
    }
866 74
867
    /**
868
     * Extract all cell references in range.
869 74
     *
870
     * @param string $pRange Range (e.g. A1 or A1:C10 or A1:E10 A20:E25)
871
     *
872 74
     * @return array Array containing single cell references
873 74
     */
874
    public static function extractAllCellReferencesInRange($pRange)
875 74
    {
876 59
        // Returnvalue
877 59
        $returnValue = [];
878
879
        // Explode spaces
880
        $cellBlocks = explode(' ', str_replace('$', '', strtoupper($pRange)));
881 66
        foreach ($cellBlocks as $cellBlock) {
882 66
            // Single cell?
883
            if (strpos($cellBlock, ':') === false && strpos($cellBlock, ',') === false) {
884 66
                $returnValue[] = $cellBlock;
885
886
                continue;
887
            }
888
889
            // Range...
890 66
            $ranges = self::splitRange($cellBlock);
891 66
            foreach ($ranges as $range) {
892 66
                // Single cell?
893 66
                if (!isset($range[1])) {
894
                    $returnValue[] = $range[0];
895
896 66
                    continue;
897 66
                }
898
899
                // Range...
900 66
                [$rangeStart, $rangeEnd] = $range;
0 ignored issues
show
Bug introduced by
The variable $rangeStart does not exist. Did you mean $ranges?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
Bug introduced by
The variable $rangeEnd does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
901 66
                sscanf($rangeStart, '%[A-Z]%d', $startCol, $startRow);
0 ignored issues
show
Bug introduced by
The variable $rangeStart does not exist. Did you mean $ranges?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
Bug introduced by
The variable $startRow does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
902 66
                sscanf($rangeEnd, '%[A-Z]%d', $endCol, $endRow);
0 ignored issues
show
Bug introduced by
The variable $endRow does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
903 66
                ++$endCol;
904
905 66
                // Current data
906 66
                $currentCol = $startCol;
907
                $currentRow = $startRow;
908
909
                // Loop cells
910
                while ($currentCol != $endCol) {
911
                    while ($currentRow <= $endRow) {
912 74
                        $returnValue[] = $currentCol . $currentRow;
913 74
                        ++$currentRow;
914 74
                    }
915 74
                    ++$currentCol;
916
                    $currentRow = $startRow;
917 74
                }
918
            }
919
        }
920 74
921
        //    Sort the result by column and row
922
        $sortKeys = [];
923
        foreach (array_unique($returnValue) as $coord) {
924
            sscanf($coord, '%[A-Z]%d', $column, $row);
0 ignored issues
show
Bug introduced by
The variable $row does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
925
            $sortKeys[sprintf('%3s%09d', $column, $row)] = $coord;
926
        }
927
        ksort($sortKeys);
928
929
        // Return value
930
        return array_values($sortKeys);
931
    }
932
933
    /**
934
     * Convert an associative array of single cell coordinates to values to an associative array
935
     * of cell ranges to values.  Only adjacent cell coordinates with the same
936
     * value will be merged.  If the value is an object, it must implement the method getHashCode().
937
     *
938
     * For example, this function converts:
939
     *
940 6
     *    [ 'A1' => 'x', 'A2' => 'x', 'A3' => 'x', 'A4' => 'y' ]
941
     *
942 6
     * to:
943
     *
944 6
     *    [ 'A1:A3' => 'x', 'A4' => 'y' ]
945 6
     *
946 6
     * @param array $pCoordCollection associative array mapping coordinates to values
947 6
     *
948
     * @return array associative array mapping coordinate ranges to valuea
949 6
     */
950 6
    public static function mergeRangesInCollection(array $pCoordCollection)
951 6
    {
952 6
        $hashedValues = [];
953 6
954
        foreach ($pCoordCollection as $coord => $value) {
955
            [$column, $row] = self::coordinateFromString($coord);
0 ignored issues
show
Bug introduced by
The variable $column does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $row does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
956 6
            $row = (int) (ltrim($row, '$'));
957
            $hashCode = $column . '-' . (is_object($value) ? $value->getHashCode() : $value);
958
959
            if (!isset($hashedValues[$hashCode])) {
960 6
                $hashedValues[$hashCode] = (object) [
961 6
                    'value' => $value,
962
                    'col' => $column,
963 6
                    'rows' => [$row],
964 6
                ];
965 6
            } else {
966 6
                $hashedValues[$hashCode]->rows[] = $row;
967 6
            }
968
        }
969 6
970 6
        $mergedCoordCollection = [];
971 6
        ksort($hashedValues);
972 6
973 3
        foreach ($hashedValues as $hashedValue) {
974 3
            sort($hashedValue->rows);
975
            $rowStart = null;
976 1
            $rowEnd = null;
977
            $ranges = [];
978
979 1
            foreach ($hashedValue->rows as $row) {
980
                if ($rowStart === null) {
981
                    $rowStart = $row;
982 1
                    $rowEnd = $row;
983 6
                } elseif ($rowEnd === $row - 1) {
984
                    $rowEnd = $row;
985
                } else {
986
                    if ($rowStart == $rowEnd) {
987 6
                        $ranges[] = $hashedValue->col . $rowStart;
988 6
                    } else {
989 5
                        $ranges[] = $hashedValue->col . $rowStart . ':' . $hashedValue->col . $rowEnd;
990
                    }
991 2
992
                    $rowStart = $row;
993
                    $rowEnd = $row;
994
                }
995 6
            }
996 6
997
            if ($rowStart !== null) {
998
                if ($rowStart == $rowEnd) {
999
                    $ranges[] = $hashedValue->col . $rowStart;
1000 6
                } else {
1001
                    $ranges[] = $hashedValue->col . $rowStart . ':' . $hashedValue->col . $rowEnd;
1002
                }
1003
            }
1004
1005
            foreach ($ranges as $range) {
1006
                $mergedCoordCollection[$range] = $hashedValue->value;
1007
            }
1008
        }
1009
1010
        return $mergedCoordCollection;
1011
    }
1012
1013
    /**
1014
     * Compare 2 cells.
1015
     *
1016
     * @param Cell $a Cell a
1017
     * @param Cell $b Cell b
1018
     *
1019
     * @return int Result of comparison (always -1 or 1, never zero!)
1020
     */
1021
    public static function compareCells(Cell $a, Cell $b)
1022
    {
1023
        if ($a->getRow() < $b->getRow()) {
1024
            return -1;
1025
        } elseif ($a->getRow() > $b->getRow()) {
1026
            return 1;
1027
        } elseif (self::columnIndexFromString($a->getColumn()) < self::columnIndexFromString($b->getColumn())) {
1028
            return -1;
1029 77
        }
1030
1031 77
        return 1;
1032 70
    }
1033
1034
    /**
1035 77
     * Get value binder to use.
1036
     *
1037
     * @return Cell\IValueBinder
1038
     */
1039
    public static function getValueBinder()
1040
    {
1041
        if (self::$valueBinder === null) {
1042
            self::$valueBinder = new Cell\DefaultValueBinder();
1043
        }
1044
1045 2
        return self::$valueBinder;
1046
    }
1047 2
1048 2
    /**
1049
     * Set value binder to use.
1050
     *
1051
     * @param Cell\IValueBinder $binder
1052
     *
1053 2
     * @throws Exception
1054
     */
1055 2
    public static function setValueBinder(Cell\IValueBinder $binder)
1056 2
    {
1057 2
        self::$valueBinder = $binder;
1058
    }
1059
1060 2
    /**
1061
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
1062
     */
1063 2
    public function __clone()
1064
    {
1065
        $vars = get_object_vars($this);
1066
        foreach ($vars as $key => $value) {
1067
            if ((is_object($value)) && ($key != 'parent')) {
1068
                $this->$key = clone $value;
1069
            } else {
1070 99
                $this->$key = $value;
1071
            }
1072 99
        }
1073
    }
1074
1075
    /**
1076
     * Get index to cellXf.
1077
     *
1078
     * @return int
1079
     */
1080
    public function getXfIndex()
1081
    {
1082 91
        return $this->xfIndex;
1083
    }
1084 91
1085
    /**
1086 91
     * Set index to cellXf.
1087
     *
1088
     * @param int $pValue
1089
     *
1090
     * @return Cell
1091
     */
1092
    public function setXfIndex($pValue)
1093
    {
1094
        $this->xfIndex = $pValue;
1095
1096
        return $this->updateInCollection();
1097
    }
1098
1099
    /**
1100
     * Set the formula attributes.
1101
     *
1102
     * @param mixed $pAttributes
1103
     */
1104 18
    public function setFormulaAttributes($pAttributes)
1105
    {
1106 18
        $this->formulaAttributes = $pAttributes;
1107
1108
        return $this;
1109
    }
1110
1111
    /**
1112
     * Get the formula attributes.
1113
     */
1114
    public function getFormulaAttributes()
1115
    {
1116
        return $this->formulaAttributes;
1117
    }
1118
1119
    /**
1120
     * Convert to string.
1121
     *
1122
     * @return string
1123
     */
1124
    public function __toString()
1125
    {
1126
        return (string) $this->getValue();
1127
    }
1128
}
1129