Passed
Push — master ( 3e93d4...2d1f4e )
by
unknown
24:41 queued 16:58
created

DataSeriesValues::refresh()   C

Complexity

Conditions 12
Paths 5

Size

Total Lines 43
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 12.0053

Importance

Changes 0
Metric Value
eloc 29
c 0
b 0
f 0
dl 0
loc 43
ccs 29
cts 30
cp 0.9667
rs 6.9666
cc 12
nc 5
nop 2
crap 12.0053

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Chart;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6
use PhpOffice\PhpSpreadsheet\Calculation\Functions;
7
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
8
use PhpOffice\PhpSpreadsheet\Worksheet\Worksheet;
9
10
class DataSeriesValues extends Properties
11
{
12
    const DATASERIES_TYPE_STRING = 'String';
13
    const DATASERIES_TYPE_NUMBER = 'Number';
14
15
    private const DATA_TYPE_VALUES = [
16
        self::DATASERIES_TYPE_STRING,
17
        self::DATASERIES_TYPE_NUMBER,
18
    ];
19
20
    /**
21
     * Series Data Type.
22
     */
23
    private string $dataType;
24
25
    /**
26
     * Series Data Source.
27
     */
28
    private ?string $dataSource;
29
30
    /**
31
     * Format Code.
32
     */
33
    private ?string $formatCode;
34
35
    /**
36
     * Series Point Marker.
37
     */
38
    private ?string $pointMarker;
39
40
    private ChartColor $markerFillColor;
41
42
    private ChartColor $markerBorderColor;
43
44
    /**
45
     * Series Point Size.
46
     */
47
    private int $pointSize = 3;
48
49
    /**
50
     * Point Count (The number of datapoints in the dataseries).
51
     */
52
    private int $pointCount;
53
54
    /**
55
     * Data Values.
56
     *
57
     * @var null|mixed[]
58
     */
59
    private ?array $dataValues;
60
61
    /**
62
     * Fill color (can be array with colors if dataseries have custom colors).
63
     *
64
     * @var null|ChartColor|ChartColor[]
65
     */
66
    private $fillColor;
67
68
    private bool $scatterLines = true;
69
70
    private bool $bubble3D = false;
71
72
    private ?Layout $labelLayout = null;
73
74
    /** @var TrendLine[] */
75
    private array $trendLines = [];
76
77
    /**
78
     * Create a new DataSeriesValues object.
79
     *
80
     * @param null|mixed[] $dataValues
81
     * @param null|ChartColor|ChartColor[]|string|string[] $fillColor
82
     */
83 118
    public function __construct(
84
        string $dataType = self::DATASERIES_TYPE_NUMBER,
85
        ?string $dataSource = null,
86
        ?string $formatCode = null,
87
        int $pointCount = 0,
88
        ?array $dataValues = [],
89
        ?string $marker = null,
90
        null|ChartColor|array|string $fillColor = null,
91
        int|string $pointSize = 3
92
    ) {
93 118
        parent::__construct();
94 118
        $this->markerFillColor = new ChartColor();
95 118
        $this->markerBorderColor = new ChartColor();
96 118
        $this->setDataType($dataType);
97 118
        $this->dataSource = $dataSource;
98 118
        $this->formatCode = $formatCode;
99 118
        $this->pointCount = $pointCount;
100 118
        $this->dataValues = $dataValues;
101 118
        $this->pointMarker = $marker;
102 118
        if ($fillColor !== null) {
103 27
            $this->setFillColor($fillColor);
104
        }
105 118
        if (is_numeric($pointSize)) {
106 115
            $this->pointSize = (int) $pointSize;
107
        }
108
    }
109
110
    /**
111
     * Get Series Data Type.
112
     */
113 85
    public function getDataType(): string
114
    {
115 85
        return $this->dataType;
116
    }
117
118
    /**
119
     * Set Series Data Type.
120
     *
121
     * @param string $dataType Datatype of this data series
122
     *                                Typical values are:
123
     *                                    DataSeriesValues::DATASERIES_TYPE_STRING
124
     *                                        Normally used for axis point values
125
     *                                    DataSeriesValues::DATASERIES_TYPE_NUMBER
126
     *                                        Normally used for chart data values
127
     *
128
     * @return $this
129
     */
130 118
    public function setDataType(string $dataType): static
131
    {
132 118
        if (!in_array($dataType, self::DATA_TYPE_VALUES)) {
133 1
            throw new Exception('Invalid datatype for chart data series values');
134
        }
135 118
        $this->dataType = $dataType;
136
137 118
        return $this;
138
    }
139
140
    /**
141
     * Get Series Data Source (formula).
142
     */
143 93
    public function getDataSource(): ?string
144
    {
145 93
        return $this->dataSource;
146
    }
147
148
    /**
149
     * Set Series Data Source (formula).
150
     *
151
     * @return $this
152
     */
153 1
    public function setDataSource(?string $dataSource): static
154
    {
155 1
        $this->dataSource = $dataSource;
156
157 1
        return $this;
158
    }
159
160
    /**
161
     * Get Point Marker.
162
     */
163 95
    public function getPointMarker(): ?string
164
    {
165 95
        return $this->pointMarker;
166
    }
167
168
    /**
169
     * Set Point Marker.
170
     *
171
     * @return $this
172
     */
173 4
    public function setPointMarker(string $marker): static
174
    {
175 4
        $this->pointMarker = $marker;
176
177 4
        return $this;
178
    }
179
180 96
    public function getMarkerFillColor(): ChartColor
181
    {
182 96
        return $this->markerFillColor;
183
    }
184
185 96
    public function getMarkerBorderColor(): ChartColor
186
    {
187 96
        return $this->markerBorderColor;
188
    }
189
190
    /**
191
     * Get Point Size.
192
     */
193 27
    public function getPointSize(): int
194
    {
195 27
        return $this->pointSize;
196
    }
197
198
    /**
199
     * Set Point Size.
200
     *
201
     * @return $this
202
     */
203 4
    public function setPointSize(int $size = 3): static
204
    {
205 4
        $this->pointSize = $size;
206
207 4
        return $this;
208
    }
209
210
    /**
211
     * Get Series Format Code.
212
     */
213 88
    public function getFormatCode(): ?string
214
    {
215 88
        return $this->formatCode;
216
    }
217
218
    /**
219
     * Set Series Format Code.
220
     *
221
     * @return $this
222
     */
223 67
    public function setFormatCode(string $formatCode): static
224
    {
225 67
        $this->formatCode = $formatCode;
226
227 67
        return $this;
228
    }
229
230
    /**
231
     * Get Series Point Count.
232
     */
233 95
    public function getPointCount(): int
234
    {
235 95
        return $this->pointCount;
236
    }
237
238
    /**
239
     * Get fill color object.
240
     *
241
     * @return null|ChartColor|ChartColor[]
242
     */
243 91
    public function getFillColorObject()
244
    {
245 91
        return $this->fillColor;
246
    }
247
248 16
    private function stringToChartColor(string $fillString): ChartColor
249
    {
250 16
        $value = $type = '';
251 16
        if (str_starts_with($fillString, '*')) {
252 5
            $type = 'schemeClr';
253 5
            $value = substr($fillString, 1);
254 14
        } elseif (str_starts_with($fillString, '/')) {
255 3
            $type = 'prstClr';
256 3
            $value = substr($fillString, 1);
257 14
        } elseif ($fillString !== '') {
258 14
            $type = 'srgbClr';
259 14
            $value = $fillString;
260 14
            $this->validateColor($value);
261
        }
262
263 15
        return new ChartColor($value, null, $type);
264
    }
265
266 4
    private function chartColorToString(ChartColor $chartColor): string
267
    {
268 4
        $type = (string) $chartColor->getColorProperty('type');
269 4
        $value = (string) $chartColor->getColorProperty('value');
270 4
        if ($type === '' || $value === '') {
271 1
            return '';
272
        }
273 4
        if ($type === 'schemeClr') {
274 3
            return "*$value";
275
        }
276 4
        if ($type === 'prstClr') {
277 3
            return "/$value";
278
        }
279
280 4
        return $value;
281
    }
282
283
    /**
284
     * Get fill color.
285
     *
286
     * @return string|string[] HEX color or array with HEX colors
287
     */
288 9
    public function getFillColor(): string|array
289
    {
290 9
        if ($this->fillColor === null) {
291 5
            return '';
292
        }
293 4
        if (is_array($this->fillColor)) {
294 4
            $array = [];
295 4
            foreach ($this->fillColor as $chartColor) {
296 4
                $array[] = $this->chartColorToString($chartColor);
297
            }
298
299 4
            return $array;
300
        }
301
302 1
        return $this->chartColorToString($this->fillColor);
303
    }
304
305
    /**
306
     * Set fill color for series.
307
     *
308
     * @param ChartColor|ChartColor[]|string|string[] $color HEX color or array with HEX colors
309
     *
310
     * @return   $this
311
     */
312 38
    public function setFillColor($color): static
313
    {
314 38
        if (is_array($color)) {
315 16
            $this->fillColor = [];
316 16
            foreach ($color as $fillString) {
317 16
                if ($fillString instanceof ChartColor) {
318 9
                    $this->fillColor[] = $fillString;
319
                } else {
320 9
                    $this->fillColor[] = $this->stringToChartColor($fillString);
321
                }
322
            }
323 29
        } elseif ($color instanceof ChartColor) {
324 21
            $this->fillColor = $color;
325
        } else {
326 8
            $this->fillColor = $this->stringToChartColor($color);
327
        }
328
329 36
        return $this;
330
    }
331
332
    /**
333
     * Method for validating hex color.
334
     *
335
     * @param string $color value for color
336
     *
337
     * @return bool true if validation was successful
338
     */
339 14
    private function validateColor(string $color): bool
340
    {
341 14
        if (!preg_match('/^[a-f0-9]{6}$/i', $color)) {
342 2
            throw new Exception(sprintf('Invalid hex color for chart series (color: "%s")', $color));
343
        }
344
345 13
        return true;
346
    }
347
348
    /**
349
     * Get line width for series.
350
     */
351 6
    public function getLineWidth(): null|float|int
352
    {
353
        /** @var null|float|int */
354 6
        $temp = $this->lineStyleProperties['width'];
355
356 6
        return $temp;
357
    }
358
359
    /**
360
     * Set line width for the series.
361
     *
362
     * @return $this
363
     */
364 7
    public function setLineWidth(null|float|int $width): static
365
    {
366 7
        $this->lineStyleProperties['width'] = $width;
367
368 7
        return $this;
369
    }
370
371
    /**
372
     * Identify if the Data Series is a multi-level or a simple series.
373
     */
374 91
    public function isMultiLevelSeries(): ?bool
375
    {
376 91
        if (!empty($this->dataValues)) {
377 91
            return is_array(array_values($this->dataValues)[0]);
378
        }
379
380 2
        return null;
381
    }
382
383
    /**
384
     * Return the level count of a multi-level Data Series.
385
     */
386 18
    public function multiLevelCount(): int
387
    {
388 18
        $levelCount = 0;
389 18
        foreach (($this->dataValues ?? []) as $dataValueSet) {
390
            /** @var mixed[] $dataValueSet */
391 18
            $levelCount = max($levelCount, count($dataValueSet));
392
        }
393
394 18
        return $levelCount;
395
    }
396
397
    /**
398
     * Get Series Data Values.
399
     *
400
     * @return null|mixed[]
401
     */
402 95
    public function getDataValues(): ?array
403
    {
404 95
        return $this->dataValues;
405
    }
406
407
    /**
408
     * Get the first Series Data value.
409
     */
410 6
    public function getDataValue(): mixed
411
    {
412 6
        if ($this->dataValues === null) {
413
            return null;
414
        }
415 6
        $count = count($this->dataValues);
416 6
        if ($count == 0) {
417 3
            return null;
418 5
        } elseif ($count == 1) {
419 5
            return $this->dataValues[0];
420
        }
421
422 1
        return $this->dataValues;
423
    }
424
425
    /**
426
     * Set Series Data Values.
427
     *
428
     * @param mixed[] $dataValues
429
     *
430
     * @return $this
431
     */
432 67
    public function setDataValues(array $dataValues): static
433
    {
434 67
        $this->dataValues = Functions::flattenArray($dataValues);
435 67
        $this->pointCount = count($dataValues);
436
437 67
        return $this;
438
    }
439
440 95
    public function refresh(Worksheet $worksheet, bool $flatten = true): void
441
    {
442 95
        if ($this->dataSource !== null) {
443 94
            $calcEngine = Calculation::getInstance($worksheet->getParent());
444 94
            $newDataValues = Calculation::unwrapResult(
445 94
                $calcEngine->_calculateFormulaValue(
446 94
                    '=' . $this->dataSource,
447 94
                    null,
448 94
                    $worksheet->getCell('A1')
449 94
                )
450 94
            );
451 94
            if ($flatten) {
452 94
                $this->dataValues = Functions::flattenArray($newDataValues);
453 94
                foreach ($this->dataValues as &$dataValue) {
454 94
                    if (is_string($dataValue) && !empty($dataValue) && $dataValue[0] == '#') {
455
                        $dataValue = 0.0;
456
                    }
457
                }
458 94
                unset($dataValue);
459
            } else {
460 85
                [, $cellRange] = Worksheet::extractSheetTitle($this->dataSource, true);
461 85
                $dimensions = Coordinate::rangeDimension(str_replace('$', '', $cellRange ?? ''));
462 85
                if (($dimensions[0] == 1) || ($dimensions[1] == 1)) {
463 68
                    $this->dataValues = Functions::flattenArray($newDataValues);
464
                } else {
465
                    /** @var array<int, mixed[]> */
466 21
                    $newDataValuesx = $newDataValues;
467
                    /** @var mixed[][] $newArray */
468 21
                    $newArray = array_values(array_shift($newDataValuesx) ?? []);
469 21
                    foreach ($newArray as $i => $newDataSet) {
470 21
                        $newArray[$i] = [$newDataSet];
471
                    }
472
473 21
                    foreach ($newDataValuesx as $newDataSet) {
474 21
                        $i = 0;
475 21
                        foreach ($newDataSet as $newDataVal) {
476 21
                            array_unshift($newArray[$i++], $newDataVal);
477
                        }
478
                    }
479 21
                    $this->dataValues = $newArray;
480
                }
481
            }
482 94
            $this->pointCount = count($this->dataValues ?? []);
483
        }
484
    }
485
486 45
    public function getScatterLines(): bool
487
    {
488 45
        return $this->scatterLines;
489
    }
490
491 28
    public function setScatterLines(bool $scatterLines): self
492
    {
493 28
        $this->scatterLines = $scatterLines;
494
495 28
        return $this;
496
    }
497
498 3
    public function getBubble3D(): bool
499
    {
500 3
        return $this->bubble3D;
501
    }
502
503 2
    public function setBubble3D(bool $bubble3D): self
504
    {
505 2
        $this->bubble3D = $bubble3D;
506
507 2
        return $this;
508
    }
509
510
    /**
511
     * Smooth Line. Must be specified for both DataSeries and DataSeriesValues.
512
     */
513
    private bool $smoothLine = false;
514
515
    /**
516
     * Get Smooth Line.
517
     */
518 11
    public function getSmoothLine(): bool
519
    {
520 11
        return $this->smoothLine;
521
    }
522
523
    /**
524
     * Set Smooth Line.
525
     *
526
     * @return $this
527
     */
528 15
    public function setSmoothLine(bool $smoothLine): static
529
    {
530 15
        $this->smoothLine = $smoothLine;
531
532 15
        return $this;
533
    }
534
535 91
    public function getLabelLayout(): ?Layout
536
    {
537 91
        return $this->labelLayout;
538
    }
539
540 9
    public function setLabelLayout(?Layout $labelLayout): self
541
    {
542 9
        $this->labelLayout = $labelLayout;
543
544 9
        return $this;
545
    }
546
547
    /** @param TrendLine[] $trendLines */
548 7
    public function setTrendLines(array $trendLines): self
549
    {
550 7
        $this->trendLines = $trendLines;
551
552 7
        return $this;
553
    }
554
555
    /** @return TrendLine[] */
556 92
    public function getTrendLines(): array
557
    {
558 92
        return $this->trendLines;
559
    }
560
561
    /**
562
     * Implement PHP __clone to create a deep clone, not just a shallow copy.
563
     */
564 5
    public function __clone()
565
    {
566 5
        parent::__clone();
567 5
        $this->markerFillColor = clone $this->markerFillColor;
568 5
        $this->markerBorderColor = clone $this->markerBorderColor;
569 5
        if (is_array($this->fillColor)) {
570 1
            $fillColor = $this->fillColor;
571 1
            $this->fillColor = [];
572 1
            foreach ($fillColor as $color) {
573 1
                $this->fillColor[] = clone $color;
574
            }
575 5
        } elseif ($this->fillColor instanceof ChartColor) {
576 1
            $this->fillColor = clone $this->fillColor;
577
        }
578 5
        $this->labelLayout = ($this->labelLayout === null) ? null : clone $this->labelLayout;
579 5
        $trendLines = $this->trendLines;
580 5
        $this->trendLines = [];
581 5
        foreach ($trendLines as $trendLine) {
582 1
            $this->trendLines[] = clone $trendLine;
583
        }
584
    }
585
}
586