Passed
Push — master ( a950d1...8a122f )
by Mark
14:29
created

Cell::getRow()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 0
crap 2
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Cell;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6
use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
7
use PhpOffice\PhpSpreadsheet\Collection\Cells;
8
use PhpOffice\PhpSpreadsheet\Exception;
9
use PhpOffice\PhpSpreadsheet\RichText\RichText;
10
use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDate;
11
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
12
use PhpOffice\PhpSpreadsheet\Style\ConditionalFormatting\CellStyleAssessor;
13
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
14
use PhpOffice\PhpSpreadsheet\Style\Style;
15
use PhpOffice\PhpSpreadsheet\Worksheet\Table;
16
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
17
18
class Cell
19
{
20
    /**
21
     * Value binder to use.
22
     *
23
     * @var IValueBinder
24
     */
25
    private static $valueBinder;
26
27
    /**
28
     * Value of the cell.
29
     *
30
     * @var mixed
31
     */
32
    private $value;
33
34
    /**
35
     *    Calculated value of the cell (used for caching)
36
     *    This returns the value last calculated by MS Excel or whichever spreadsheet program was used to
37
     *        create the original spreadsheet file.
38
     *    Note that this value is not guaranteed to reflect the actual calculated value because it is
39
     *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data
40
     *        values used by the formula have changed since it was last calculated.
41
     *
42
     * @var mixed
43
     */
44
    private $calculatedValue;
45
46
    /**
47
     * Type of the cell data.
48
     *
49
     * @var string
50
     */
51
    private $dataType;
52
53
    /**
54
     * The collection of cells that this cell belongs to (i.e. The Cell Collection for the parent Worksheet).
55
     *
56
     * @var ?Cells
57
     */
58
    private $parent;
59
60
    /**
61
     * Index to the cellXf reference for the styling of this cell.
62
     *
63
     * @var int
64
     */
65
    private $xfIndex = 0;
66
67
    /**
68
     * Attributes of the formula.
69
     *
70
     * @var mixed
71
     */
72
    private $formulaAttributes;
73
74
    /**
75
     * Update the cell into the cell collection.
76
     *
77
     * @return $this
78
     */
79 9750
    public function updateInCollection(): self
80
    {
81 9750
        $parent = $this->parent;
82 9750
        if ($parent === null) {
83 2
            throw new Exception('Cannot update when cell is not bound to a worksheet');
84
        }
85 9748
        $parent->update($this);
86
87 9748
        return $this;
88
    }
89
90 9324
    public function detach(): void
91
    {
92 9324
        $this->parent = null;
93
    }
94
95 8753
    public function attach(Cells $parent): void
96
    {
97 8753
        $this->parent = $parent;
98
    }
99
100
    /**
101
     * Create a new Cell.
102
     *
103
     * @param mixed $value
104
     */
105 9799
    public function __construct($value, ?string $dataType, Worksheet $worksheet)
106
    {
107
        // Initialise cell value
108 9799
        $this->value = $value;
109
110
        // Set worksheet cache
111 9799
        $this->parent = $worksheet->getCellCollection();
112
113
        // Set datatype?
114 9799
        if ($dataType !== null) {
115 9799
            if ($dataType == DataType::TYPE_STRING2) {
116
                $dataType = DataType::TYPE_STRING;
117
            }
118 9799
            $this->dataType = $dataType;
119
        } elseif (self::getValueBinder()->bindValue($this, $value) === false) {
120
            throw new Exception('Value could not be bound to cell.');
121
        }
122
    }
123
124
    /**
125
     * Get cell coordinate column.
126
     *
127
     * @return string
128
     */
129 494
    public function getColumn()
130
    {
131 494
        $parent = $this->parent;
132 494
        if ($parent === null) {
133 1
            throw new Exception('Cannot get column when cell is not bound to a worksheet');
134
        }
135
136 493
        return $parent->getCurrentColumn();
137
    }
138
139
    /**
140
     * Get cell coordinate row.
141
     *
142
     * @return int
143
     */
144 478
    public function getRow()
145
    {
146 478
        $parent = $this->parent;
147 478
        if ($parent === null) {
148 1
            throw new Exception('Cannot get row when cell is not bound to a worksheet');
149
        }
150
151 477
        return $parent->getCurrentRow();
152
    }
153
154
    /**
155
     * Get cell coordinate.
156
     *
157
     * @return string
158
     */
159 9755
    public function getCoordinate()
160
    {
161 9755
        $parent = $this->parent;
162 9755
        if ($parent !== null) {
163 9755
            $coordinate = $parent->getCurrentCoordinate();
164
        } else {
165 2
            $coordinate = null;
166
        }
167 9755
        if ($coordinate === null) {
168 2
            throw new Exception('Coordinate no longer exists');
169
        }
170
171 9755
        return $coordinate;
172
    }
173
174
    /**
175
     * Get cell value.
176
     *
177
     * @return mixed
178
     */
179 9202
    public function getValue()
180
    {
181 9202
        return $this->value;
182
    }
183
184
    /**
185
     * Get cell value with formatting.
186
     */
187 82
    public function getFormattedValue(): string
188
    {
189 82
        return (string) NumberFormat::toFormattedString(
190 82
            $this->getCalculatedValue(),
191 82
            (string) $this->getStyle()->getNumberFormat()->getFormatCode()
192 82
        );
193
    }
194
195
    /**
196
     * @param mixed $oldValue
197
     * @param mixed $newValue
198
     */
199 9722
    protected static function updateIfCellIsTableHeader(?Worksheet $workSheet, self $cell, $oldValue, $newValue): void
200
    {
201 9722
        if (StringHelper::strToLower($oldValue ?? '') === StringHelper::strToLower($newValue ?? '') || $workSheet === null) {
202 726
            return;
203
        }
204
205 9688
        foreach ($workSheet->getTableCollection() as $table) {
206
            /** @var Table $table */
207 8
            if ($cell->isInRange($table->getRange())) {
208 6
                $rangeRowsColumns = Coordinate::getRangeBoundaries($table->getRange());
209 6
                if ($cell->getRow() === (int) $rangeRowsColumns[0][1]) {
210 3
                    Table\Column::updateStructuredReferences($workSheet, $oldValue, $newValue);
211
                }
212
213 6
                return;
214
            }
215
        }
216
    }
217
218
    /**
219
     * Set cell value.
220
     *
221
     *    Sets the value for a cell, automatically determining the datatype using the value binder
222
     *
223
     * @param mixed $value Value
224
     *
225
     * @return $this
226
     */
227 9492
    public function setValue($value): self
228
    {
229 9492
        if (!self::getValueBinder()->bindValue($this, $value)) {
230
            throw new Exception('Value could not be bound to cell.');
231
        }
232
233 9491
        return $this;
234
    }
235
236
    /**
237
     * Set the value for a cell, with the explicit data type passed to the method (bypassing any use of the value binder).
238
     *
239
     * @param mixed $value Value
240
     * @param string $dataType Explicit data type, see DataType::TYPE_*
241
     *        Note that PhpSpreadsheet does not validate that the value and datatype are consistent, in using this
242
     *             method, then it is your responsibility as an end-user developer to validate that the value and
243
     *             the datatype match.
244
     *       If you do mismatch value and datatype, then the value you enter may be changed to match the datatype
245
     *          that you specify.
246
     *
247
     * @return Cell
248
     */
249 9725
    public function setValueExplicit($value, string $dataType = DataType::TYPE_STRING)
250
    {
251 9725
        $oldValue = $this->value;
252
253
        // set the value according to data type
254
        switch ($dataType) {
255 255
            case DataType::TYPE_NULL:
256 474
                $this->value = null;
257
258 474
                break;
259 254
            case DataType::TYPE_STRING2:
260 2
                $dataType = DataType::TYPE_STRING;
261
                // no break
262 254
            case DataType::TYPE_STRING:
263
                // Synonym for string
264 230
            case DataType::TYPE_INLINE:
265
                // Rich text
266 5238
                $this->value = DataType::checkString($value);
267
268 5238
                break;
269 229
            case DataType::TYPE_NUMERIC:
270 6598
                if (is_string($value) && !is_numeric($value)) {
271 1
                    throw new Exception('Invalid numeric value for datatype Numeric');
272
                }
273 6597
                $this->value = 0 + $value;
274
275 6597
                break;
276 190
            case DataType::TYPE_FORMULA:
277 8441
                $this->value = (string) $value;
278
279 8441
                break;
280 17
            case DataType::TYPE_BOOL:
281 475
                $this->value = (bool) $value;
282
283 475
                break;
284
            case DataType::TYPE_ISO_DATE:
285 5
                $this->value = SharedDate::convertIsoDate($value);
286 4
                $dataType = DataType::TYPE_NUMERIC;
287
288 4
                break;
289
            case DataType::TYPE_ERROR:
290 19
                $this->value = DataType::checkErrorCode($value);
291
292 19
                break;
293
            default:
294
                throw new Exception('Invalid datatype: ' . $dataType);
295
        }
296
297
        // set the datatype
298 9723
        $this->dataType = $dataType;
299
300 9723
        $this->updateInCollection();
301 9722
        $cellCoordinate = $this->getCoordinate();
302 9722
        self::updateIfCellIsTableHeader($this->getParent()->getParent(), $this, $oldValue, $value); // @phpstan-ignore-line
303
304 9722
        return $this->getParent()->get($cellCoordinate); // @phpstan-ignore-line
305
    }
306
307
    public const CALCULATE_DATE_TIME_ASIS = 0;
308
    public const CALCULATE_DATE_TIME_FLOAT = 1;
309
    public const CALCULATE_TIME_FLOAT = 2;
310
311
    /** @var int */
312
    private static $calculateDateTimeType = self::CALCULATE_DATE_TIME_ASIS;
313
314 45
    public static function getCalculateDateTimeType(): int
315
    {
316 45
        return self::$calculateDateTimeType;
317
    }
318
319 45
    public static function setCalculateDateTimeType(int $calculateDateTimeType): void
320
    {
321
        switch ($calculateDateTimeType) {
322 45
            case self::CALCULATE_DATE_TIME_ASIS:
323 23
            case self::CALCULATE_DATE_TIME_FLOAT:
324 12
            case self::CALCULATE_TIME_FLOAT:
325 45
                self::$calculateDateTimeType = $calculateDateTimeType;
326
327 45
                break;
328
            default:
329 1
                throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception("Invalid value $calculateDateTimeType for calculated date time type");
330
        }
331
    }
332
333
    /**
334
     * Convert date, time, or datetime from int to float if desired.
335
     *
336
     * @param mixed $result
337
     *
338
     * @return mixed
339
     */
340 8754
    private function convertDateTimeInt($result)
341
    {
342 8754
        if (is_int($result)) {
343 4200
            if (self::$calculateDateTimeType === self::CALCULATE_TIME_FLOAT) {
344 4
                if (SharedDate::isDateTime($this, $result, false)) {
345 4
                    $result = (float) $result;
346
                }
347 4196
            } elseif (self::$calculateDateTimeType === self::CALCULATE_DATE_TIME_FLOAT) {
348 4
                if (SharedDate::isDateTime($this, $result, true)) {
349 3
                    $result = (float) $result;
350
                }
351
            }
352
        }
353
354 8754
        return $result;
355
    }
356
357
    /**
358
     * Get calculated cell value.
359
     *
360
     * @param bool $resetLog Whether the calculation engine logger should be reset or not
361
     *
362
     * @return mixed
363
     */
364 8955
    public function getCalculatedValue(bool $resetLog = true)
365
    {
366 8955
        if ($this->dataType === DataType::TYPE_FORMULA) {
367
            try {
368 8153
                $index = $this->getWorksheet()->getParentOrThrow()->getActiveSheetIndex();
369 8153
                $selected = $this->getWorksheet()->getSelectedCells();
370 8153
                $result = Calculation::getInstance(
371 8153
                    $this->getWorksheet()->getParent()
372 8153
                )->calculateCellValue($this, $resetLog);
373 7950
                $result = $this->convertDateTimeInt($result);
374 7950
                $this->getWorksheet()->setSelectedCells($selected);
375 7950
                $this->getWorksheet()->getParentOrThrow()->setActiveSheetIndex($index);
376
                //    We don't yet handle array returns
377 7950
                if (is_array($result)) {
378 7950
                    while (is_array($result)) {
379 3727
                        $result = array_shift($result);
380
                    }
381
                }
382 219
            } catch (Exception $ex) {
383 219
                if (($ex->getMessage() === 'Unable to access External Workbook') && ($this->calculatedValue !== null)) {
384 1
                    return $this->calculatedValue; // Fallback for calculations referencing external files.
385 219
                } elseif (preg_match('/[Uu]ndefined (name|offset: 2|array key 2)/', $ex->getMessage()) === 1) {
386 28
                    return ExcelError::NAME();
387
                }
388
389 191
                throw new \PhpOffice\PhpSpreadsheet\Calculation\Exception(
390 191
                    $this->getWorksheet()->getTitle() . '!' . $this->getCoordinate() . ' -> ' . $ex->getMessage()
391 191
                );
392
            }
393
394 7950
            if ($result === '#Not Yet Implemented') {
395 2
                return $this->calculatedValue; // Fallback if calculation engine does not support the formula.
396
            }
397
398 7950
            return $result;
399 6258
        } elseif ($this->value instanceof RichText) {
400 18
            return $this->value->getPlainText();
401
        }
402
403 6253
        return $this->convertDateTimeInt($this->value);
404
    }
405
406
    /**
407
     * Set old calculated value (cached).
408
     *
409
     * @param mixed $originalValue Value
410
     */
411 357
    public function setCalculatedValue($originalValue): self
412
    {
413 357
        if ($originalValue !== null) {
414 357
            $this->calculatedValue = (is_numeric($originalValue)) ? (float) $originalValue : $originalValue;
415
        }
416
417 357
        return $this->updateInCollection();
418
    }
419
420
    /**
421
     *    Get old calculated value (cached)
422
     *    This returns the value last calculated by MS Excel or whichever spreadsheet program was used to
423
     *        create the original spreadsheet file.
424
     *    Note that this value is not guaranteed to reflect the actual calculated value because it is
425
     *        possible that auto-calculation was disabled in the original spreadsheet, and underlying data
426
     *        values used by the formula have changed since it was last calculated.
427
     *
428
     * @return mixed
429
     */
430 3
    public function getOldCalculatedValue()
431
    {
432 3
        return $this->calculatedValue;
433
    }
434
435
    /**
436
     * Get cell data type.
437
     */
438 922
    public function getDataType(): string
439
    {
440 922
        return $this->dataType;
441
    }
442
443
    /**
444
     * Set cell data type.
445
     *
446
     * @param string $dataType see DataType::TYPE_*
447
     */
448
    public function setDataType($dataType): self
449
    {
450
        if ($dataType == DataType::TYPE_STRING2) {
451
            $dataType = DataType::TYPE_STRING;
452
        }
453
        $this->dataType = $dataType;
454
455
        return $this->updateInCollection();
456
    }
457
458
    /**
459
     * Identify if the cell contains a formula.
460
     */
461 64
    public function isFormula(): bool
462
    {
463 64
        return $this->dataType === DataType::TYPE_FORMULA && $this->getStyle()->getQuotePrefix() === false;
464
    }
465
466
    /**
467
     *    Does this cell contain Data validation rules?
468
     */
469 13
    public function hasDataValidation(): bool
470
    {
471 13
        if (!isset($this->parent)) {
472 1
            throw new Exception('Cannot check for data validation when cell is not bound to a worksheet');
473
        }
474
475 12
        return $this->getWorksheet()->dataValidationExists($this->getCoordinate());
476
    }
477
478
    /**
479
     * Get Data validation rules.
480
     */
481 23
    public function getDataValidation(): DataValidation
482
    {
483 23
        if (!isset($this->parent)) {
484 1
            throw new Exception('Cannot get data validation for cell that is not bound to a worksheet');
485
        }
486
487 22
        return $this->getWorksheet()->getDataValidation($this->getCoordinate());
488
    }
489
490
    /**
491
     * Set Data validation rules.
492
     */
493 1
    public function setDataValidation(?DataValidation $dataValidation = null): self
494
    {
495 1
        if (!isset($this->parent)) {
496 1
            throw new Exception('Cannot set data validation for cell that is not bound to a worksheet');
497
        }
498
499
        $this->getWorksheet()->setDataValidation($this->getCoordinate(), $dataValidation);
500
501
        return $this->updateInCollection();
502
    }
503
504
    /**
505
     * Does this cell contain valid value?
506
     */
507 4
    public function hasValidValue(): bool
508
    {
509 4
        $validator = new DataValidator();
510
511 4
        return $validator->isValid($this);
512
    }
513
514
    /**
515
     * Does this cell contain a Hyperlink?
516
     */
517 1
    public function hasHyperlink(): bool
518
    {
519 1
        if (!isset($this->parent)) {
520 1
            throw new Exception('Cannot check for hyperlink when cell is not bound to a worksheet');
521
        }
522
523
        return $this->getWorksheet()->hyperlinkExists($this->getCoordinate());
524
    }
525
526
    /**
527
     * Get Hyperlink.
528
     */
529 58
    public function getHyperlink(): Hyperlink
530
    {
531 58
        if (!isset($this->parent)) {
532 1
            throw new Exception('Cannot get hyperlink for cell that is not bound to a worksheet');
533
        }
534
535 57
        return $this->getWorksheet()->getHyperlink($this->getCoordinate());
536
    }
537
538
    /**
539
     * Set Hyperlink.
540
     */
541 1
    public function setHyperlink(?Hyperlink $hyperlink = null): self
542
    {
543 1
        if (!isset($this->parent)) {
544 1
            throw new Exception('Cannot set hyperlink for cell that is not bound to a worksheet');
545
        }
546
547
        $this->getWorksheet()->setHyperlink($this->getCoordinate(), $hyperlink);
548
549
        return $this->updateInCollection();
550
    }
551
552
    /**
553
     * Get cell collection.
554
     *
555
     * @return ?Cells
556
     */
557 9724
    public function getParent()
558
    {
559 9724
        return $this->parent;
560
    }
561
562
    /**
563
     * Get parent worksheet.
564
     */
565 8991
    public function getWorksheet(): Worksheet
566
    {
567 8991
        $parent = $this->parent;
568 8991
        if ($parent !== null) {
569 8991
            $worksheet = $parent->getParent();
570
        } else {
571 1
            $worksheet = null;
572
        }
573
574 8991
        if ($worksheet === null) {
575 1
            throw new Exception('Worksheet no longer exists');
576
        }
577
578 8991
        return $worksheet;
579
    }
580
581 9
    public function getWorksheetOrNull(): ?Worksheet
582
    {
583 9
        $parent = $this->parent;
584 9
        if ($parent !== null) {
585 9
            $worksheet = $parent->getParent();
586
        } else {
587
            $worksheet = null;
588
        }
589
590 9
        return $worksheet;
591
    }
592
593
    /**
594
     * Is this cell in a merge range.
595
     */
596 7
    public function isInMergeRange(): bool
597
    {
598 7
        return (bool) $this->getMergeRange();
599
    }
600
601
    /**
602
     * Is this cell the master (top left cell) in a merge range (that holds the actual data value).
603
     */
604 26
    public function isMergeRangeValueCell(): bool
605
    {
606 26
        if ($mergeRange = $this->getMergeRange()) {
607 5
            $mergeRange = Coordinate::splitRange($mergeRange);
608 5
            [$startCell] = $mergeRange[0];
609
610 5
            return $this->getCoordinate() === $startCell;
611
        }
612
613 23
        return false;
614
    }
615
616
    /**
617
     * If this cell is in a merge range, then return the range.
618
     *
619
     * @return false|string
620
     */
621 29
    public function getMergeRange()
622
    {
623 29
        foreach ($this->getWorksheet()->getMergeCells() as $mergeRange) {
624 5
            if ($this->isInRange($mergeRange)) {
625 5
                return $mergeRange;
626
            }
627
        }
628
629 26
        return false;
630
    }
631
632
    /**
633
     * Get cell style.
634
     */
635 8878
    public function getStyle(): Style
636
    {
637 8878
        return $this->getWorksheet()->getStyle($this->getCoordinate());
638
    }
639
640
    /**
641
     * Get cell style.
642
     */
643 6
    public function getAppliedStyle(): Style
644
    {
645 6
        if ($this->getWorksheet()->conditionalStylesExists($this->getCoordinate()) === false) {
646 2
            return $this->getStyle();
647
        }
648 4
        $range = $this->getWorksheet()->getConditionalRange($this->getCoordinate());
649 4
        if ($range === null) {
650
            return $this->getStyle();
651
        }
652
653 4
        $matcher = new CellStyleAssessor($this, $range);
654
655 4
        return $matcher->matchConditions($this->getWorksheet()->getConditionalStyles($this->getCoordinate()));
656
    }
657
658
    /**
659
     * Re-bind parent.
660
     */
661
    public function rebindParent(Worksheet $parent): self
662
    {
663
        $this->parent = $parent->getCellCollection();
664
665
        return $this->updateInCollection();
666
    }
667
668
    /**
669
     *    Is cell in a specific range?
670
     *
671
     * @param string $range Cell range (e.g. A1:A1)
672
     */
673 227
    public function isInRange(string $range): bool
674
    {
675 227
        [$rangeStart, $rangeEnd] = Coordinate::rangeBoundaries($range);
676
677
        // Translate properties
678 227
        $myColumn = Coordinate::columnIndexFromString($this->getColumn());
679 227
        $myRow = $this->getRow();
680
681
        // Verify if cell is in range
682 227
        return ($rangeStart[0] <= $myColumn) && ($rangeEnd[0] >= $myColumn) &&
683 227
                ($rangeStart[1] <= $myRow) && ($rangeEnd[1] >= $myRow);
684
    }
685
686
    /**
687
     * Compare 2 cells.
688
     *
689
     * @param Cell $a Cell a
690
     * @param Cell $b Cell b
691
     *
692
     * @return int Result of comparison (always -1 or 1, never zero!)
693
     */
694
    public static function compareCells(self $a, self $b): int
695
    {
696
        if ($a->getRow() < $b->getRow()) {
697
            return -1;
698
        } elseif ($a->getRow() > $b->getRow()) {
699
            return 1;
700
        } elseif (Coordinate::columnIndexFromString($a->getColumn()) < Coordinate::columnIndexFromString($b->getColumn())) {
701
            return -1;
702
        }
703
704
        return 1;
705
    }
706
707
    /**
708
     * Get value binder to use.
709
     */
710 9493
    public static function getValueBinder(): IValueBinder
711
    {
712 9493
        if (self::$valueBinder === null) {
713 231
            self::$valueBinder = new DefaultValueBinder();
714
        }
715
716 9493
        return self::$valueBinder;
717
    }
718
719
    /**
720
     * Set value binder to use.
721
     */
722 107
    public static function setValueBinder(IValueBinder $binder): void
723
    {
724 107
        self::$valueBinder = $binder;
725
    }
726
727
    /**
728
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
729
     */
730 6
    public function __clone()
731
    {
732 6
        $vars = get_object_vars($this);
733 6
        foreach ($vars as $propertyName => $propertyValue) {
734 6
            if ((is_object($propertyValue)) && ($propertyName !== 'parent')) {
735
                $this->$propertyName = clone $propertyValue;
736
            } else {
737 6
                $this->$propertyName = $propertyValue;
738
            }
739
        }
740
    }
741
742
    /**
743
     * Get index to cellXf.
744
     */
745 9256
    public function getXfIndex(): int
746
    {
747 9256
        return $this->xfIndex;
748
    }
749
750
    /**
751
     * Set index to cellXf.
752
     */
753 1394
    public function setXfIndex(int $indexValue): self
754
    {
755 1394
        $this->xfIndex = $indexValue;
756
757 1394
        return $this->updateInCollection();
758
    }
759
760
    /**
761
     * Set the formula attributes.
762
     *
763
     * @param mixed $attributes
764
     *
765
     * @return $this
766
     */
767
    public function setFormulaAttributes($attributes): self
768
    {
769
        $this->formulaAttributes = $attributes;
770
771
        return $this;
772
    }
773
774
    /**
775
     * Get the formula attributes.
776
     *
777
     * @return mixed
778
     */
779 70
    public function getFormulaAttributes()
780
    {
781 70
        return $this->formulaAttributes;
782
    }
783
784
    /**
785
     * Convert to string.
786
     *
787
     * @return string
788
     */
789
    public function __toString()
790
    {
791
        return (string) $this->getValue();
792
    }
793
}
794