PluginProbe
TablePress – Tables in WordPress made easy / 3.4
TablePress – Tables in WordPress made easy v3.4
3.4 3.3.4 3.3.3 3.3.2 3.3.1 trunk 1.12 1.14 1.9.2 2.0.4 2.1.7 2.1.8 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.3 2.3.1 2.3.2 2.4 2.4.1 2.4.2 2.4.3 All 45 releases
tablepress / libraries / vendor / PhpSpreadsheet / Chart / DataSeriesValues.php

DataSeriesValues.php in TablePress – Tables in WordPress made easy 3.4, at libraries/vendor/PhpSpreadsheet/Chart/DataSeriesValues.php

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