Passed
Pull Request — master (#4426)
by
unknown
13:11
created

Style::getConditionalStyles()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
ccs 0
cts 0
cp 0
crap 2
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Style;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
6
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
7
use PhpOffice\PhpSpreadsheet\Exception;
8
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
9
use PhpOffice\PhpSpreadsheet\Spreadsheet;
10
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
11
12
class Style extends Supervisor
13
{
14
    /**
15
     * Font.
16
     */
17
    protected Font $font;
18
19
    /**
20
     * Fill.
21
     */
22
    protected Fill $fill;
23
24
    /**
25
     * Borders.
26
     */
27
    protected Borders $borders;
28
29
    /**
30
     * Alignment.
31
     */
32
    protected Alignment $alignment;
33
34
    /**
35
     * Number Format.
36
     */
37
    protected NumberFormat $numberFormat;
38
39
    /**
40
     * Protection.
41
     */
42
    protected Protection $protection;
43
44
    /**
45
     * Index of style in collection. Only used for real style.
46
     */
47
    protected int $index;
48
49
    /**
50
     * Use Quote Prefix when displaying in cell editor. Only used for real style.
51
     */
52
    protected bool $quotePrefix = false;
53
54
    /**
55
     * Internal cache for styles
56
     * Used when applying style on range of cells (column or row) and cleared when
57
     * all cells in range is styled.
58
     *
59
     * PhpSpreadsheet will always minimize the amount of styles used. So cells with
60
     * same styles will reference the same Style instance. To check if two styles
61
     * are similar Style::getHashCode() is used. This call is expensive. To minimize
62
     * the need to call this method we can cache the internal PHP object id of the
63
     * Style in the range. Style::getHashCode() will then only be called when we
64
     * encounter a unique style.
65
     *
66
     * @see Style::applyFromArray()
67
     * @see Style::getHashCode()
68
     *
69
     * @var null|array<string, array>
70
     */
71
    private static ?array $cachedStyles = null;
72
73
    /**
74
     * Create a new Style.
75
     *
76
     * @param bool $isSupervisor Flag indicating if this is a supervisor or not
77
     *         Leave this value at default unless you understand exactly what
78
     *    its ramifications are
79
     * @param bool $isConditional Flag indicating if this is a conditional style or not
80
     *       Leave this value at default unless you understand exactly what
81
     *    its ramifications are
82
     */
83 10568
    public function __construct(bool $isSupervisor = false, bool $isConditional = false)
84
    {
85 10568
        parent::__construct($isSupervisor);
86
87
        // Initialise values
88 10568
        $this->font = new Font($isSupervisor, $isConditional);
89 10568
        $this->fill = new Fill($isSupervisor, $isConditional);
90 10568
        $this->borders = new Borders($isSupervisor, $isConditional);
91 10568
        $this->alignment = new Alignment($isSupervisor, $isConditional);
92 10568
        $this->numberFormat = new NumberFormat($isSupervisor, $isConditional);
93 10568
        $this->protection = new Protection($isSupervisor, $isConditional);
94
95
        // bind parent if we are a supervisor
96 10568
        if ($isSupervisor) {
97 10505
            $this->font->bindParent($this);
98 10505
            $this->fill->bindParent($this);
99 10505
            $this->borders->bindParent($this);
100 10505
            $this->alignment->bindParent($this);
101 10505
            $this->numberFormat->bindParent($this);
102 10505
            $this->protection->bindParent($this);
103
        }
104
    }
105
106
    /**
107
     * Get the shared style component for the currently active cell in currently active sheet.
108
     * Only used for style supervisor.
109
     */
110 10073
    public function getSharedComponent(): self
111
    {
112 10073
        $activeSheet = $this->getActiveSheet();
113 10073
        $selectedCell = Functions::trimSheetFromCellReference($this->getActiveCell()); // e.g. 'A1'
114
115 10073
        if ($activeSheet->cellExists($selectedCell)) {
116 10073
            $xfIndex = $activeSheet->getCell($selectedCell)->getXfIndex();
117
        } else {
118 6
            $xfIndex = 0;
119
        }
120
121 10073
        return $activeSheet->getParentOrThrow()->getCellXfByIndex($xfIndex);
122
    }
123
124
    /**
125
     * Get parent. Only used for style supervisor.
126
     */
127 1
    public function getParent(): Spreadsheet
128
    {
129 1
        return $this->getActiveSheet()->getParentOrThrow();
130
    }
131
132
    /**
133
     * Build style array from subcomponents.
134
     */
135 1
    public function getStyleArray(array $array): array
136
    {
137 1
        return ['quotePrefix' => $array];
138
    }
139
140
    /**
141
     * Apply styles from array.
142
     *
143
     * <code>
144
     * $spreadsheet->getActiveSheet()->getStyle('B2')->applyFromArray(
145
     *     [
146
     *         'font' => [
147
     *             'name' => 'Arial',
148
     *             'bold' => true,
149
     *             'italic' => false,
150
     *             'underline' => Font::UNDERLINE_DOUBLE,
151
     *             'strikethrough' => false,
152
     *             'color' => [
153
     *                 'rgb' => '808080'
154
     *             ]
155
     *         ],
156
     *         'borders' => [
157
     *             'bottom' => [
158
     *                 'borderStyle' => Border::BORDER_DASHDOT,
159
     *                 'color' => [
160
     *                     'rgb' => '808080'
161
     *                 ]
162
     *             ],
163
     *             'top' => [
164
     *                 'borderStyle' => Border::BORDER_DASHDOT,
165
     *                 'color' => [
166
     *                     'rgb' => '808080'
167
     *                 ]
168
     *             ]
169
     *         ],
170
     *         'alignment' => [
171
     *             'horizontal' => Alignment::HORIZONTAL_CENTER,
172
     *             'vertical' => Alignment::VERTICAL_CENTER,
173
     *             'wrapText' => true,
174
     *         ],
175
     *         'quotePrefix'    => true
176
     *     ]
177
     * );
178
     * </code>
179
     *
180
     * @param array $styleArray Array containing style information
181
     * @param bool $advancedBorders advanced mode for setting borders
182
     *
183
     * @return $this
184
     */
185 960
    public function applyFromArray(array $styleArray, bool $advancedBorders = true): static
186
    {
187 960
        if ($this->isSupervisor) {
188 906
            $pRange = $this->getSelectedCells();
189
190
            // Uppercase coordinate and strip any Worksheet reference from the selected range
191 906
            $pRange = strtoupper($pRange);
192 906
            if (str_contains($pRange, '!')) {
193 5
                $pRangeWorksheet = StringHelper::strToUpper(substr($pRange, 0, (int) strrpos($pRange, '!')));
194 5
                $pRangeWorksheet = Worksheet::unApostrophizeTitle($pRangeWorksheet);
195 5
                if ($pRangeWorksheet !== '' && StringHelper::strToUpper($this->getActiveSheet()->getTitle()) !== $pRangeWorksheet) {
196 2
                    throw new Exception('Invalid Worksheet for specified Range');
197
                }
198 3
                $pRange = strtoupper(Functions::trimSheetFromCellReference($pRange));
199
            }
200
201
            // Is it a cell range or a single cell?
202 904
            if (!str_contains($pRange, ':')) {
203 787
                $rangeA = $pRange;
204 787
                $rangeB = $pRange;
205
            } else {
206 218
                [$rangeA, $rangeB] = explode(':', $pRange);
207
            }
208
209
            // Calculate range outer borders
210 904
            $rangeStart = Coordinate::coordinateFromString($rangeA);
211 896
            $rangeEnd = Coordinate::coordinateFromString($rangeB);
212 896
            $rangeStartIndexes = Coordinate::indexesFromString($rangeA);
213 896
            $rangeEndIndexes = Coordinate::indexesFromString($rangeB);
214
215 896
            $columnStart = $rangeStart[0];
216 896
            $columnEnd = $rangeEnd[0];
217
218
            // Make sure we can loop upwards on rows and columns
219 896
            if ($rangeStartIndexes[0] > $rangeEndIndexes[0] && $rangeStartIndexes[1] > $rangeEndIndexes[1]) {
220 1
                $tmp = $rangeStartIndexes;
221 1
                $rangeStartIndexes = $rangeEndIndexes;
222 1
                $rangeEndIndexes = $tmp;
223
            }
224
225
            // ADVANCED MODE:
226 896
            if ($advancedBorders && isset($styleArray['borders'])) {
227
                // 'allBorders' is a shorthand property for 'outline' and 'inside' and
228
                //        it applies to components that have not been set explicitly
229 87
                if (isset($styleArray['borders']['allBorders'])) {
230 10
                    foreach (['outline', 'inside'] as $component) {
231 10
                        if (!isset($styleArray['borders'][$component])) {
232 10
                            $styleArray['borders'][$component] = $styleArray['borders']['allBorders'];
233
                        }
234
                    }
235 10
                    unset($styleArray['borders']['allBorders']); // not needed any more
236
                }
237
                // 'outline' is a shorthand property for 'top', 'right', 'bottom', 'left'
238
                //        it applies to components that have not been set explicitly
239 87
                if (isset($styleArray['borders']['outline'])) {
240 32
                    foreach (['top', 'right', 'bottom', 'left'] as $component) {
241 32
                        if (!isset($styleArray['borders'][$component])) {
242 32
                            $styleArray['borders'][$component] = $styleArray['borders']['outline'];
243
                        }
244
                    }
245 32
                    unset($styleArray['borders']['outline']); // not needed any more
246
                }
247
                // 'inside' is a shorthand property for 'vertical' and 'horizontal'
248
                //        it applies to components that have not been set explicitly
249 87
                if (isset($styleArray['borders']['inside'])) {
250 11
                    foreach (['vertical', 'horizontal'] as $component) {
251 11
                        if (!isset($styleArray['borders'][$component])) {
252 11
                            $styleArray['borders'][$component] = $styleArray['borders']['inside'];
253
                        }
254
                    }
255 11
                    unset($styleArray['borders']['inside']); // not needed any more
256
                }
257
                // width and height characteristics of selection, 1, 2, or 3 (for 3 or more)
258 87
                $xMax = min($rangeEndIndexes[0] - $rangeStartIndexes[0] + 1, 3);
259 87
                $yMax = min($rangeEndIndexes[1] - $rangeStartIndexes[1] + 1, 3);
260
261
                // loop through up to 3 x 3 = 9 regions
262 87
                for ($x = 1; $x <= $xMax; ++$x) {
263
                    // start column index for region
264 87
                    $colStart = ($x == 3)
265 22
                        ? Coordinate::stringFromColumnIndex($rangeEndIndexes[0])
266 87
                        : Coordinate::stringFromColumnIndex($rangeStartIndexes[0] + $x - 1);
267
                    // end column index for region
268 87
                    $colEnd = ($x == 1)
269 87
                        ? Coordinate::stringFromColumnIndex($rangeStartIndexes[0])
270 48
                        : Coordinate::stringFromColumnIndex($rangeEndIndexes[0] - $xMax + $x);
271
272 87
                    for ($y = 1; $y <= $yMax; ++$y) {
273
                        // which edges are touching the region
274 87
                        $edges = [];
275 87
                        if ($x == 1) {
276
                            // are we at left edge
277 87
                            $edges[] = 'left';
278
                        }
279 87
                        if ($x == $xMax) {
280
                            // are we at right edge
281 87
                            $edges[] = 'right';
282
                        }
283 87
                        if ($y == 1) {
284
                            // are we at top edge?
285 87
                            $edges[] = 'top';
286
                        }
287 87
                        if ($y == $yMax) {
288
                            // are we at bottom edge?
289 87
                            $edges[] = 'bottom';
290
                        }
291
292
                        // start row index for region
293 87
                        $rowStart = ($y == 3)
294 87
                            ? $rangeEndIndexes[1] : $rangeStartIndexes[1] + $y - 1;
295
296
                        // end row index for region
297 87
                        $rowEnd = ($y == 1)
298 87
                            ? $rangeStartIndexes[1] : $rangeEndIndexes[1] - $yMax + $y;
299
300
                        // build range for region
301 87
                        $range = $colStart . $rowStart . ':' . $colEnd . $rowEnd;
302
303
                        // retrieve relevant style array for region
304 87
                        $regionStyles = $styleArray;
305 87
                        unset($regionStyles['borders']['inside']);
306
307
                        // what are the inner edges of the region when looking at the selection
308 87
                        $innerEdges = array_diff(['top', 'right', 'bottom', 'left'], $edges);
309
310
                        // inner edges that are not touching the region should take the 'inside' border properties if they have been set
311 87
                        foreach ($innerEdges as $innerEdge) {
312
                            switch ($innerEdge) {
313 48
                                case 'top':
314 48
                                case 'bottom':
315
                                    // should pick up 'horizontal' border property if set
316 46
                                    if (isset($styleArray['borders']['horizontal'])) {
317 8
                                        $regionStyles['borders'][$innerEdge] = $styleArray['borders']['horizontal'];
318
                                    } else {
319 38
                                        unset($regionStyles['borders'][$innerEdge]);
320
                                    }
321
322 46
                                    break;
323 48
                                case 'left':
324 48
                                case 'right':
325
                                    // should pick up 'vertical' border property if set
326 48
                                    if (isset($styleArray['borders']['vertical'])) {
327 9
                                        $regionStyles['borders'][$innerEdge] = $styleArray['borders']['vertical'];
328
                                    } else {
329 39
                                        unset($regionStyles['borders'][$innerEdge]);
330
                                    }
331
332 48
                                    break;
333
                            }
334
                        }
335
336
                        // apply region style to region by calling applyFromArray() in simple mode
337 87
                        $this->getActiveSheet()->getStyle($range)->applyFromArray($regionStyles, false);
338
                    }
339
                }
340
341
                // restore initial cell selection range
342 87
                $this->getActiveSheet()->getStyle($pRange);
343
                $this->updateHashBeforeUse();
344 87
345
                return $this;
346
            }
347
348
            // SIMPLE MODE:
349 896
            // Selection type, inspect
350 7
            if (preg_match('/^[A-Z]+1:[A-Z]+1048576$/', $pRange)) {
351
                $selectionType = 'COLUMN';
352
353 7
                // Enable caching of styles
354 894
                self::$cachedStyles = ['hashByObjId' => [], 'styleByHash' => []];
355 3
            } elseif (preg_match('/^A\d+:XFD\d+$/', $pRange)) {
356
                $selectionType = 'ROW';
357
358 3
                // Enable caching of styles
359
                self::$cachedStyles = ['hashByObjId' => [], 'styleByHash' => []];
360 892
            } else {
361
                $selectionType = 'CELL';
362
            }
363
364 896
            // First loop through columns, rows, or cells to find out which styles are affected by this operation
365
            $oldXfIndexes = $this->getOldXfIndexes($selectionType, $rangeStartIndexes, $rangeEndIndexes, $columnStart, $columnEnd, $styleArray);
366
367 896
            // clone each of the affected styles, apply the style array, and add the new styles to the workbook
368 896
            $workbook = $this->getActiveSheet()->getParentOrThrow();
369 896
            $newXfIndexes = [];
370 896
            foreach ($oldXfIndexes as $oldXfIndex => $dummy) {
371
                $style = $workbook->getCellXfByIndex($oldXfIndex);
372
373 896
                // $cachedStyles is set when applying style for a range of cells, either column or row
374
                if (self::$cachedStyles === null) {
375 890
                    // Clone the old style and apply style-array
376 890
                    $newStyle = clone $style;
377
                    $newStyle->applyFromArray($styleArray);
378
379 890
                    // Look for existing style we can use instead (reduce memory usage)
380
                    $existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode());
381
                } else {
382
                    // Style cache is stored by Style::getHashCode(). But calling this method is
383 10
                    // expensive. So we cache the php obj id -> hash.
384
                    $objId = spl_object_id($style);
385
386 10
                    // Look for the original HashCode
387 10
                    $styleHash = self::$cachedStyles['hashByObjId'][$objId] ?? null;
388
                    if ($styleHash === null) {
389 10
                        // This object_id is not cached, store the hashcode in case encounter again
390
                        $styleHash = self::$cachedStyles['hashByObjId'][$objId] = $style->getHashCode();
391
                    }
392
393 10
                    // Find existing style by hash.
394
                    $existingStyle = self::$cachedStyles['styleByHash'][$styleHash] ?? null;
395 10
396
                    if (!$existingStyle) {
397 10
                        // The old style combined with the new style array is not cached, so we create it now
398 10
                        $newStyle = clone $style;
399
                        $newStyle->applyFromArray($styleArray);
400
401 10
                        // Look for similar style in workbook to reduce memory usage
402
                        $existingStyle = $workbook->getCellXfByHashCode($newStyle->getHashCode());
403
404 10
                        // Cache the new style by original hashcode
405
                        self::$cachedStyles['styleByHash'][$styleHash] = $existingStyle instanceof self ? $existingStyle : $newStyle;
406
                    }
407
                }
408 896
409
                if ($existingStyle) {
410 260
                    // there is already such cell Xf in our collection
411
                    $newXfIndexes[$oldXfIndex] = $existingStyle->getIndex();
412 840
                } else {
413
                    if (!isset($newStyle)) {
414
                        // Handle bug in PHPStan, see https://github.com/phpstan/phpstan/issues/5805
415
                        // $newStyle should always be defined.
416
                        // This block might not be needed in the future
417
                        // @codeCoverageIgnoreStart
418
                        $newStyle = clone $style;
419
                        $newStyle->applyFromArray($styleArray);
420
                        // @codeCoverageIgnoreEnd
421
                    }
422
423 840
                    // we don't have such a cell Xf, need to add
424 840
                    $workbook->addCellXf($newStyle);
425
                    $newXfIndexes[$oldXfIndex] = $newStyle->getIndex();
426
                }
427
            }
428
429
            // Loop through columns, rows, or cells again and update the XF index
430 896
            switch ($selectionType) {
431 7
                case 'COLUMN':
432 7
                    for ($col = $rangeStartIndexes[0]; $col <= $rangeEndIndexes[0]; ++$col) {
433 7
                        $columnDimension = $this->getActiveSheet()->getColumnDimensionByColumn($col);
434 7
                        $oldXfIndex = $columnDimension->getXfIndex();
435
                        $columnDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
436
                    }
437
438 7
                    // Disable caching of styles
439
                    self::$cachedStyles = null;
440 7
441 894
                    break;
442 3
                case 'ROW':
443 3
                    for ($row = $rangeStartIndexes[1]; $row <= $rangeEndIndexes[1]; ++$row) {
444
                        $rowDimension = $this->getActiveSheet()->getRowDimension($row);
445 3
                        // row without explicit style should be formatted based on default style
446 3
                        $oldXfIndex = $rowDimension->getXfIndex() ?? 0;
447
                        $rowDimension->setXfIndex($newXfIndexes[$oldXfIndex]);
448
                    }
449
450 3
                    // Disable caching of styles
451
                    self::$cachedStyles = null;
452 3
453 892
                    break;
454 892
                case 'CELL':
455 892
                    for ($col = $rangeStartIndexes[0]; $col <= $rangeEndIndexes[0]; ++$col) {
456 892
                        for ($row = $rangeStartIndexes[1]; $row <= $rangeEndIndexes[1]; ++$row) {
457 892
                            $cell = $this->getActiveSheet()->getCell([$col, $row]);
458 892
                            $oldXfIndex = $cell->getXfIndex();
459
                            $cell->setXfIndex($newXfIndexes[$oldXfIndex]);
460
                        }
461
                    }
462 892
463
                    break;
464
            }
465
        } else {
466 950
            // not a supervisor, just apply the style array directly on style object
467 132
            if (isset($styleArray['fill'])) {
468
                $this->getFill()->applyFromArray($styleArray['fill']);
469 950
            }
470 190
            if (isset($styleArray['font'])) {
471
                $this->getFont()->applyFromArray($styleArray['font']);
472 950
            }
473 93
            if (isset($styleArray['borders'])) {
474
                $this->getBorders()->applyFromArray($styleArray['borders']);
475 950
            }
476 129
            if (isset($styleArray['alignment'])) {
477
                $this->getAlignment()->applyFromArray($styleArray['alignment']);
478 950
            }
479 712
            if (isset($styleArray['numberFormat'])) {
480
                $this->getNumberFormat()->applyFromArray($styleArray['numberFormat']);
481 950
            }
482 38
            if (isset($styleArray['protection'])) {
483
                $this->getProtection()->applyFromArray($styleArray['protection']);
484 950
            }
485 56
            if (isset($styleArray['quotePrefix'])) {
486
                $this->quotePrefix = $styleArray['quotePrefix'];
487
            }
488
        }
489 950
        $this->updateHashBeforeUse();
490
491
        return $this;
492 896
    }
493
494 896
    private function getOldXfIndexes(string $selectionType, array $rangeStart, array $rangeEnd, string $columnStart, string $columnEnd, array $styleArray): array
495
    {
496 896
        $oldXfIndexes = [];
497 7
        switch ($selectionType) {
498 7
            case 'COLUMN':
499
                for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
500 7
                    $oldXfIndexes[$this->getActiveSheet()->getColumnDimensionByColumn($col)->getXfIndex()] = true;
501 7
                }
502 7
                foreach ($this->getActiveSheet()->getColumnIterator($columnStart, $columnEnd) as $columnIterator) {
503 7
                    $cellIterator = $columnIterator->getCellIterator();
504 4
                    $cellIterator->setIterateOnlyExistingCells(true);
505 4
                    foreach ($cellIterator as $columnCell) {
506
                        $columnCell->getStyle()
507
                            ->applyFromArray($styleArray);
508
                    }
509 7
                }
510 894
511 3
                break;
512 3
            case 'ROW':
513 3
                for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
514
                    if ($this->getActiveSheet()->getRowDimension($row)->getXfIndex() === null) {
515 1
                        $oldXfIndexes[0] = true; // row without explicit style should be formatted based on default style
516
                    } else {
517
                        $oldXfIndexes[$this->getActiveSheet()->getRowDimension($row)->getXfIndex()] = true;
518 3
                    }
519 3
                }
520 3
                foreach ($this->getActiveSheet()->getRowIterator((int) $rangeStart[1], (int) $rangeEnd[1]) as $rowIterator) {
521 3
                    $cellIterator = $rowIterator->getCellIterator();
522 1
                    $cellIterator->setIterateOnlyExistingCells(true);
523 1
                    foreach ($cellIterator as $rowCell) {
524
                        $rowCell->getStyle()
525
                            ->applyFromArray($styleArray);
526
                    }
527 3
                }
528 892
529 892
                break;
530 892
            case 'CELL':
531 892
                for ($col = $rangeStart[0]; $col <= $rangeEnd[0]; ++$col) {
532
                    for ($row = $rangeStart[1]; $row <= $rangeEnd[1]; ++$row) {
533
                        $oldXfIndexes[$this->getActiveSheet()->getCell([$col, $row])->getXfIndex()] = true;
534
                    }
535 892
                }
536
537
                break;
538 896
        }
539
540
        return $oldXfIndexes;
541
    }
542
543
    /**
544 1553
     * Get Fill.
545
     */
546 1553
    public function getFill(): Fill
547
    {
548
        return $this->fill;
549
    }
550
551
    /**
552 1574
     * Get Font.
553
     */
554 1574
    public function getFont(): Font
555
    {
556
        return $this->font;
557
    }
558
559
    /**
560
     * Set font.
561
     *
562 116
     * @return $this
563
     */
564 116
    public function setFont(Font $font): static
565
    {
566 116
        $this->font = $font;
567
        $this->updateHashBeforeUse();
568
569
        return $this;
570
    }
571
572 1503
    /**
573
     * Get Borders.
574 1503
     */
575
    public function getBorders(): Borders
576
    {
577
        return $this->borders;
578
    }
579
580 1535
    /**
581
     * Get Alignment.
582 1535
     */
583
    public function getAlignment(): Alignment
584
    {
585
        return $this->alignment;
586
    }
587
588 1780
    /**
589
     * Get Number Format.
590 1780
     */
591
    public function getNumberFormat(): NumberFormat
592
    {
593
        return $this->numberFormat;
594
    }
595
596
    /**
597
     * Get Conditional Styles. Only used on supervisor.
598 584
     *
599
     * @return Conditional[]
600 584
     */
601
    public function getConditionalStyles(): array
602
    {
603
        return $this->getActiveSheet()->getConditionalStyles($this->getActiveCell());
604
    }
605
606
    /**
607
     * Set Conditional Styles. Only used on supervisor.
608
     *
609
     * @param Conditional[] $conditionalStyleArray Array of conditional styles
610 281
     *
611
     * @return $this
612 281
     */
613
    public function setConditionalStyles(array $conditionalStyleArray): static
614 281
    {
615
        $this->getActiveSheet()->setConditionalStyles($this->getSelectedCells(), $conditionalStyleArray);
616
617
        return $this;
618
    }
619
620 546
    /**
621
     * Get Protection.
622 546
     */
623
    public function getProtection(): Protection
624
    {
625
        return $this->protection;
626
    }
627
628 10101
    /**
629
     * Get quote prefix.
630 10101
     */
631 10058
    public function getQuotePrefix(): bool
632
    {
633
        if ($this->isSupervisor) {
634 10101
            return $this->getSharedComponent()->getQuotePrefix();
635
        }
636
637
        return $this->quotePrefix;
638
    }
639
640
    /**
641
     * Set quote prefix.
642 710
     *
643
     * @return $this
644 710
     */
645 672
    public function setQuotePrefix(bool $quotePrefix): static
646
    {
647 710
        if ($quotePrefix == '') {
648 45
            $quotePrefix = false;
649 45
        }
650
        if ($this->isSupervisor) {
651 672
            $styleArray = ['quotePrefix' => $quotePrefix];
652
            $this->getActiveSheet()->getStyle($this->getSelectedCells())->applyFromArray($styleArray);
653
        } else {
654 710
            $this->quotePrefix = (bool) $quotePrefix;
655
        }
656
        $this->updateHashBeforeUse();
657
658
        return $this;
659
    }
660
661
    /**
662 1245
     * Update Hash when something changes.
663
     */
664 1245
    protected function updateHash(): void
665 1245
    {
666 1245
        $this->md5Sum = md5(
667 1245
            $this->fill->getHashCode()
668 1245
            . $this->font->getHashCode()
669 1245
            . $this->borders->getHashCode()
670 1245
            . $this->alignment->getHashCode()
671 1245
            . $this->numberFormat->getHashCode()
672 1245
            . $this->protection->getHashCode()
673 1245
            . ($this->quotePrefix ? 't' : 'f')
674
            . __CLASS__
675
        );
676
        $this->updateMd5Sum = false;
677
    }
678
679 906
    /**
680
     * Get hash code.
681 906
     *
682
     * @return string Hash code
683
     */
684
    public function getHashCode(): string
685
    {
686
        if ($this->updateMd5Sum) {
687 10505
            $this->updateHash();
688
        }
689 10505
690
        return $this->md5Sum;
691
    }
692 14
693
    /**
694 14
     * Get own index in style collection.
695 14
     */
696 14
    public function getIndex(): int
697 14
    {
698 14
        return $this->index;
699 14
    }
700 14
701 14
    /**
702
     * Set own index in style collection.
703 14
     */
704
    public function setIndex(int $index): void
705
    {
706
        $this->index = $index;
707
    }
708
709
    protected function exportArray1(): array
710
    {
711
        $exportedArray = [];
712
        $this->exportArray2($exportedArray, 'alignment', $this->getAlignment());
713
        $this->exportArray2($exportedArray, 'borders', $this->getBorders());
714
        $this->exportArray2($exportedArray, 'fill', $this->getFill());
715
        $this->exportArray2($exportedArray, 'font', $this->getFont());
716
        $this->exportArray2($exportedArray, 'numberFormat', $this->getNumberFormat());
717
        $this->exportArray2($exportedArray, 'protection', $this->getProtection());
718
        $this->exportArray2($exportedArray, 'quotePrefix', $this->getQuotePrefix());
719
720
        return $exportedArray;
721
    }
722
}
723