Styles   F
last analyzed

Complexity

Total Complexity 96

Size/Duplication

Total Lines 417
Duplicated Lines 0 %

Test Coverage

Coverage 97.27%

Importance

Changes 0
Metric Value
wmc 96
eloc 217
dl 0
loc 417
ccs 214
cts 220
cp 0.9727
rs 2
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A setTheme() 0 3 1
A setStyleXml() 0 3 1
A readProtectionLocked() 0 16 5
A readProtectionHidden() 0 16 5
B readStyle() 0 33 8
B readColor() 0 27 8
B dxfs() 0 30 9
A styles() 0 3 1
A formatGeneral() 0 7 2
F readFontStyle() 0 50 18
A getAttribute() 0 13 3
A setWorkbookPalette() 0 3 1
A readAlignmentStyle() 0 25 4
B readFillStyle() 0 34 8
A readNumberFormat() 0 10 3
B readBorderStyle() 0 32 9
A setStyleBaseData() 0 5 1
A readBorder() 0 10 3
A setNamespace() 0 3 1
A getStyleAttributes() 0 8 3
A getArrayItem() 0 3 2

How to fix   Complexity   

Complex Class

Complex classes like Styles often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Styles, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Reader\Xlsx;
4
5
use PhpOffice\PhpSpreadsheet\Reader\Xlsx;
6
use PhpOffice\PhpSpreadsheet\Style\Alignment;
7
use PhpOffice\PhpSpreadsheet\Style\Border;
8
use PhpOffice\PhpSpreadsheet\Style\Borders;
9
use PhpOffice\PhpSpreadsheet\Style\Color;
10
use PhpOffice\PhpSpreadsheet\Style\Fill;
11
use PhpOffice\PhpSpreadsheet\Style\Font;
12
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
13
use PhpOffice\PhpSpreadsheet\Style\Protection;
14
use PhpOffice\PhpSpreadsheet\Style\Style;
15
use SimpleXMLElement;
16
use stdClass;
17
18
class Styles extends BaseParserClass
19
{
20
    /**
21
     * Theme instance.
22
     *
23
     * @var ?Theme
24
     */
25
    private ?Theme $theme = null;
26
27
    private array $workbookPalette = [];
28
29
    private array $styles = [];
30
31
    private array $cellStyles = [];
32
33
    private SimpleXMLElement $styleXml;
34
35
    private string $namespace = '';
36
37 589
    public function setNamespace(string $namespace): void
38
    {
39 589
        $this->namespace = $namespace;
40
    }
41
42 589
    public function setWorkbookPalette(array $palette): void
43
    {
44 589
        $this->workbookPalette = $palette;
45
    }
46
47 592
    private function getStyleAttributes(SimpleXMLElement $value): SimpleXMLElement
48
    {
49 592
        $attr = $value->attributes('');
50 592
        if ($attr === null || count($attr) === 0) {
51 588
            $attr = $value->attributes($this->namespace);
52
        }
53
54 592
        return Xlsx::testSimpleXml($attr);
55
    }
56
57 589
    public function setStyleXml(SimpleXMLElement $styleXml): void
58
    {
59 589
        $this->styleXml = $styleXml;
60
    }
61
62 574
    public function setTheme(Theme $theme): void
63
    {
64 574
        $this->theme = $theme;
65
    }
66
67 589
    public function setStyleBaseData(?Theme $theme = null, array $styles = [], array $cellStyles = []): void
68
    {
69 589
        $this->theme = $theme;
70 589
        $this->styles = $styles;
71 589
        $this->cellStyles = $cellStyles;
72
    }
73
74 588
    public function readFontStyle(Font $fontStyle, SimpleXMLElement $fontStyleXml): void
75
    {
76 588
        if (isset($fontStyleXml->name)) {
77 587
            $attr = $this->getStyleAttributes($fontStyleXml->name);
78 587
            if (isset($attr['val'])) {
79 587
                $fontStyle->setName((string) $attr['val']);
80
            }
81
        }
82 588
        if (isset($fontStyleXml->sz)) {
83 587
            $attr = $this->getStyleAttributes($fontStyleXml->sz);
84 587
            if (isset($attr['val'])) {
85 587
                $fontStyle->setSize((float) $attr['val']);
86
            }
87
        }
88 588
        if (isset($fontStyleXml->b)) {
89 461
            $attr = $this->getStyleAttributes($fontStyleXml->b);
90 461
            $fontStyle->setBold(!isset($attr['val']) || self::boolean((string) $attr['val']));
91
        }
92 588
        if (isset($fontStyleXml->i)) {
93 425
            $attr = $this->getStyleAttributes($fontStyleXml->i);
94 425
            $fontStyle->setItalic(!isset($attr['val']) || self::boolean((string) $attr['val']));
95
        }
96 588
        if (isset($fontStyleXml->strike)) {
97 407
            $attr = $this->getStyleAttributes($fontStyleXml->strike);
98 407
            $fontStyle->setStrikethrough(!isset($attr['val']) || self::boolean((string) $attr['val']));
99
        }
100 588
        $fontStyle->getColor()->setARGB($this->readColor($fontStyleXml->color));
101
102 588
        if (isset($fontStyleXml->u)) {
103 418
            $attr = $this->getStyleAttributes($fontStyleXml->u);
104 418
            if (!isset($attr['val'])) {
105 196
                $fontStyle->setUnderline(Font::UNDERLINE_SINGLE);
106
            } else {
107 402
                $fontStyle->setUnderline((string) $attr['val']);
108
            }
109
        }
110 588
        if (isset($fontStyleXml->vertAlign)) {
111 22
            $attr = $this->getStyleAttributes($fontStyleXml->vertAlign);
112 22
            if (isset($attr['val'])) {
113 22
                $verticalAlign = strtolower((string) $attr['val']);
114 22
                if ($verticalAlign === 'superscript') {
115 2
                    $fontStyle->setSuperscript(true);
116 22
                } elseif ($verticalAlign === 'subscript') {
117 2
                    $fontStyle->setSubscript(true);
118
                }
119
            }
120
        }
121 588
        if (isset($fontStyleXml->scheme)) {
122 354
            $attr = $this->getStyleAttributes($fontStyleXml->scheme);
123 354
            $fontStyle->setScheme((string) $attr['val']);
124
        }
125
    }
126
127 222
    private function readNumberFormat(NumberFormat $numfmtStyle, SimpleXMLElement $numfmtStyleXml): void
128
    {
129 222
        if ((string) $numfmtStyleXml['formatCode'] !== '') {
130
            $numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmtStyleXml['formatCode']));
131
132
            return;
133
        }
134 222
        $numfmt = $this->getStyleAttributes($numfmtStyleXml);
135 222
        if (isset($numfmt['formatCode'])) {
136 196
            $numfmtStyle->setFormatCode(self::formatGeneral((string) $numfmt['formatCode']));
137
        }
138
    }
139
140 588
    public function readFillStyle(Fill $fillStyle, SimpleXMLElement $fillStyleXml): void
141
    {
142 588
        if ($fillStyleXml->gradientFill) {
143
            /** @var SimpleXMLElement $gradientFill */
144 3
            $gradientFill = $fillStyleXml->gradientFill[0];
145 3
            $attr = $this->getStyleAttributes($gradientFill);
146 3
            if (!empty($attr['type'])) {
147 2
                $fillStyle->setFillType((string) $attr['type']);
148
            }
149 3
            $fillStyle->setRotation((float) ($attr['degree']));
150 3
            $gradientFill->registerXPathNamespace('sml', Namespaces::MAIN);
151 3
            $fillStyle->getStartColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=0]'))->color)); //* @phpstan-ignore-line
152 3
            $fillStyle->getEndColor()->setARGB($this->readColor(self::getArrayItem($gradientFill->xpath('sml:stop[@position=1]'))->color)); //* @phpstan-ignore-line
153 588
        } elseif ($fillStyleXml->patternFill) {
154 587
            $defaultFillStyle = Fill::FILL_NONE;
155 587
            if ($fillStyleXml->patternFill->fgColor) {
156 249
                $fillStyle->getStartColor()->setARGB($this->readColor($fillStyleXml->patternFill->fgColor, true));
157 249
                $defaultFillStyle = Fill::FILL_SOLID;
158
            }
159 587
            if ($fillStyleXml->patternFill->bgColor) {
160 258
                $fillStyle->getEndColor()->setARGB($this->readColor($fillStyleXml->patternFill->bgColor, true));
161 258
                $defaultFillStyle = Fill::FILL_SOLID;
162
            }
163
164 587
            $type = '';
165 587
            if ((string) $fillStyleXml->patternFill['patternType'] !== '') {
166
                $type = (string) $fillStyleXml->patternFill['patternType'];
167
            } else {
168 587
                $attr = $this->getStyleAttributes($fillStyleXml->patternFill);
169 587
                $type = (string) $attr['patternType'];
170
            }
171 587
            $patternType = ($type === '') ? $defaultFillStyle : $type;
172
173 587
            $fillStyle->setFillType($patternType);
174
        }
175
    }
176
177 588
    public function readBorderStyle(Borders $borderStyle, SimpleXMLElement $borderStyleXml): void
178
    {
179 588
        $diagonalUp = $this->getAttribute($borderStyleXml, 'diagonalUp');
180 588
        $diagonalUp = self::boolean($diagonalUp);
181 588
        $diagonalDown = $this->getAttribute($borderStyleXml, 'diagonalDown');
182 588
        $diagonalDown = self::boolean($diagonalDown);
183 588
        if ($diagonalUp === false) {
184 588
            if ($diagonalDown === false) {
185 588
                $borderStyle->setDiagonalDirection(Borders::DIAGONAL_NONE);
186
            } else {
187 588
                $borderStyle->setDiagonalDirection(Borders::DIAGONAL_DOWN);
188
            }
189 3
        } elseif ($diagonalDown === false) {
190 2
            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_UP);
191
        } else {
192 1
            $borderStyle->setDiagonalDirection(Borders::DIAGONAL_BOTH);
193
        }
194
195 588
        if (isset($borderStyleXml->left)) {
196 580
            $this->readBorder($borderStyle->getLeft(), $borderStyleXml->left);
197
        }
198 588
        if (isset($borderStyleXml->right)) {
199 579
            $this->readBorder($borderStyle->getRight(), $borderStyleXml->right);
200
        }
201 588
        if (isset($borderStyleXml->top)) {
202 581
            $this->readBorder($borderStyle->getTop(), $borderStyleXml->top);
203
        }
204 588
        if (isset($borderStyleXml->bottom)) {
205 580
            $this->readBorder($borderStyle->getBottom(), $borderStyleXml->bottom);
206
        }
207 588
        if (isset($borderStyleXml->diagonal)) {
208 577
            $this->readBorder($borderStyle->getDiagonal(), $borderStyleXml->diagonal);
209
        }
210
    }
211
212 588
    private function getAttribute(SimpleXMLElement $xml, string $attribute): string
213
    {
214 588
        $style = '';
215 588
        if ((string) $xml[$attribute] !== '') {
216
            $style = (string) $xml[$attribute];
217
        } else {
218 588
            $attr = $this->getStyleAttributes($xml);
219 588
            if (isset($attr[$attribute])) {
220 320
                $style = (string) $attr[$attribute];
221
            }
222
        }
223
224 588
        return $style;
225
    }
226
227 581
    private function readBorder(Border $border, SimpleXMLElement $borderXml): void
228
    {
229 581
        $style = $this->getAttribute($borderXml, 'style');
230 581
        if ($style !== '') {
231 72
            $border->setBorderStyle((string) $style);
232
        } else {
233 580
            $border->setBorderStyle(Border::BORDER_NONE);
234
        }
235 581
        if (isset($borderXml->color)) {
236 70
            $border->getColor()->setARGB($this->readColor($borderXml->color));
237
        }
238
    }
239
240 588
    public function readAlignmentStyle(Alignment $alignment, SimpleXMLElement $alignmentXml): void
241
    {
242 588
        $horizontal = (string) $this->getAttribute($alignmentXml, 'horizontal');
243 588
        if ($horizontal !== '') {
244 298
            $alignment->setHorizontal($horizontal);
245
        }
246 588
        $vertical = (string) $this->getAttribute($alignmentXml, 'vertical');
247 588
        if ($vertical !== '') {
248 272
            $alignment->setVertical($vertical);
249
        }
250
251 588
        $textRotation = (int) $this->getAttribute($alignmentXml, 'textRotation');
252 588
        if ($textRotation > 90) {
253 2
            $textRotation = 90 - $textRotation;
254
        }
255 588
        $alignment->setTextRotation($textRotation);
256
257 588
        $wrapText = $this->getAttribute($alignmentXml, 'wrapText');
258 588
        $alignment->setWrapText(self::boolean((string) $wrapText));
259 588
        $shrinkToFit = $this->getAttribute($alignmentXml, 'shrinkToFit');
260 588
        $alignment->setShrinkToFit(self::boolean((string) $shrinkToFit));
261 588
        $indent = (int) $this->getAttribute($alignmentXml, 'indent');
262 588
        $alignment->setIndent(max($indent, 0));
263 588
        $readingOrder = (int) $this->getAttribute($alignmentXml, 'readingOrder');
264 588
        $alignment->setReadOrder(max($readingOrder, 0));
265
    }
266
267 588
    private static function formatGeneral(string $formatString): string
268
    {
269 588
        if ($formatString === 'GENERAL') {
270 1
            $formatString = NumberFormat::FORMAT_GENERAL;
271
        }
272
273 588
        return $formatString;
274
    }
275
276
    /**
277
     * Read style.
278
     */
279 588
    public function readStyle(Style $docStyle, SimpleXMLElement|stdClass $style): void
280
    {
281 588
        if ($style instanceof SimpleXMLElement) {
282 222
            $this->readNumberFormat($docStyle->getNumberFormat(), $style->numFmt);
283
        } else {
284 588
            $docStyle->getNumberFormat()->setFormatCode(self::formatGeneral((string) $style->numFmt));
285
        }
286
287 588
        if (isset($style->font)) {
288 588
            $this->readFontStyle($docStyle->getFont(), $style->font);
289
        }
290
291 588
        if (isset($style->fill)) {
292 588
            $this->readFillStyle($docStyle->getFill(), $style->fill);
293
        }
294
295 588
        if (isset($style->border)) {
296 588
            $this->readBorderStyle($docStyle->getBorders(), $style->border);
297
        }
298
299 588
        if (isset($style->alignment)) {
300 588
            $this->readAlignmentStyle($docStyle->getAlignment(), $style->alignment);
301
        }
302
303
        // protection
304 588
        if (isset($style->protection)) {
305 588
            $this->readProtectionLocked($docStyle, $style->protection);
306 588
            $this->readProtectionHidden($docStyle, $style->protection);
307
        }
308
309
        // top-level style settings
310 588
        if (isset($style->quotePrefix)) {
311 588
            $docStyle->setQuotePrefix((bool) $style->quotePrefix);
312
        }
313
    }
314
315
    /**
316
     * Read protection locked attribute.
317
     */
318 588
    public function readProtectionLocked(Style $docStyle, SimpleXMLElement $style): void
319
    {
320 588
        $locked = '';
321 588
        if ((string) $style['locked'] !== '') {
322
            $locked = (string) $style['locked'];
323
        } else {
324 588
            $attr = $this->getStyleAttributes($style);
325 588
            if (isset($attr['locked'])) {
326 29
                $locked = (string) $attr['locked'];
327
            }
328
        }
329 588
        if ($locked !== '') {
330 29
            if (self::boolean($locked)) {
331 11
                $docStyle->getProtection()->setLocked(Protection::PROTECTION_PROTECTED);
332
            } else {
333 18
                $docStyle->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED);
334
            }
335
        }
336
    }
337
338
    /**
339
     * Read protection hidden attribute.
340
     */
341 588
    public function readProtectionHidden(Style $docStyle, SimpleXMLElement $style): void
342
    {
343 588
        $hidden = '';
344 588
        if ((string) $style['hidden'] !== '') {
345
            $hidden = (string) $style['hidden'];
346
        } else {
347 588
            $attr = $this->getStyleAttributes($style);
348 588
            if (isset($attr['hidden'])) {
349 16
                $hidden = (string) $attr['hidden'];
350
            }
351
        }
352 588
        if ($hidden !== '') {
353 16
            if (self::boolean((string) $hidden)) {
354 1
                $docStyle->getProtection()->setHidden(Protection::PROTECTION_PROTECTED);
355
            } else {
356 15
                $docStyle->getProtection()->setHidden(Protection::PROTECTION_UNPROTECTED);
357
            }
358
        }
359
    }
360
361 592
    public function readColor(SimpleXMLElement $color, bool $background = false): string
362
    {
363 592
        $attr = $this->getStyleAttributes($color);
364 592
        if (isset($attr['rgb'])) {
365 499
            return (string) $attr['rgb'];
366
        }
367 389
        if (isset($attr['indexed'])) {
368 94
            $indexedColor = (int) $attr['indexed'];
369 94
            if ($indexedColor >= count($this->workbookPalette)) {
370 91
                return Color::indexedColor($indexedColor - 7, $background)->getARGB() ?? '';
371
            }
372
373 4
            return Color::indexedColor($indexedColor, $background, $this->workbookPalette)->getARGB() ?? '';
374
        }
375 382
        if (isset($attr['theme'])) {
376 357
            if ($this->theme !== null) {
377 354
                $returnColour = $this->theme->getColourByIndex((int) $attr['theme']);
378 354
                if (isset($attr['tint'])) {
379 204
                    $tintAdjust = (float) $attr['tint'];
380 204
                    $returnColour = Color::changeBrightness($returnColour ?? '', $tintAdjust);
381
                }
382
383 354
                return 'FF' . $returnColour;
384
            }
385
        }
386
387 54
        return ($background) ? 'FFFFFFFF' : 'FF000000';
388
    }
389
390 589
    public function dxfs(bool $readDataOnly = false): array
391
    {
392 589
        $dxfs = [];
393 589
        if (!$readDataOnly && $this->styleXml) {
394
            //    Conditional Styles
395 588
            if ($this->styleXml->dxfs) {
396 573
                foreach ($this->styleXml->dxfs->dxf as $dxf) {
397 222
                    $style = new Style(false, true);
398 222
                    $this->readStyle($style, $dxf);
399 222
                    $dxfs[] = $style;
400
                }
401
            }
402
            //    Cell Styles
403 588
            if ($this->styleXml->cellStyles) {
404 584
                foreach ($this->styleXml->cellStyles->cellStyle as $cellStylex) {
405 584
                    $cellStyle = Xlsx::getAttributes($cellStylex);
406 584
                    if ((int) ($cellStyle['builtinId']) == 0) {
407 584
                        if (isset($this->cellStyles[(int) ($cellStyle['xfId'])])) {
408
                            // Set default style
409 584
                            $style = new Style();
410 584
                            $this->readStyle($style, $this->cellStyles[(int) ($cellStyle['xfId'])]);
411
412
                            // normal style, currently not using it for anything
413
                        }
414
                    }
415
                }
416
            }
417
        }
418
419 589
        return $dxfs;
420
    }
421
422 589
    public function styles(): array
423
    {
424 589
        return $this->styles;
425
    }
426
427
    /**
428
     * Get array item.
429
     *
430
     * @param mixed $array (usually array, in theory can be false)
431
     */
432 3
    private static function getArrayItem(mixed $array): ?SimpleXMLElement
433
    {
434 3
        return is_array($array) ? ($array[0] ?? null) : null;
435
    }
436
}
437