Passed
Push — spaghetti ( f02577...d84f00 )
by simpletoimplement
02:21
created

Styles::processCellXfs()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 4
eloc 8
c 1
b 0
f 0
nc 4
nop 1
dl 0
loc 14
rs 10
1
<?php declare(strict_types = 1);
2
3
namespace Spaghetti\XLSXParser;
4
5
use XMLReader;
6
7
use function in_array;
8
use function preg_match;
9
10
/**
11
 * @internal
12
 */
13
final class Styles extends AbstractXMLDictionary
14
{
15
    public const FORMAT_DATE = 1;
16
    private const FORMAT_DEFAULT = 0;
17
18
    private array $nativeDateFormats = [14, 15, 16, 17, 18, 19, 20, 21, 22, ];
19
    private array $numberFormats = [];
20
    private bool $inXfs = false;
21
    private bool $needsRewind;
22
23
    protected function readNext(): void
24
    {
25
        $xml = $this->getXMLReader();
26
27
        while ($xml->read()) {
28
            if ($this->processCellXfs(xml: $xml)) {
29
                continue;
30
            }
31
32
            $this->xfs(xml: $xml);
33
        }
34
35
        $this->valid = false;
36
        $this->closeXMLReader();
37
    }
38
39
    protected function createXMLReader(): XMLReader
40
    {
41
        $xml = parent::createXMLReader();
42
        $this->needsRewind = false;
43
44
        while ($xml->read()) {
45
            if (XMLReader::END_ELEMENT === $xml->nodeType && 'numFmts' === $xml->name) {
46
                break;
47
            }
48
49
            $this->process(xml: $xml);
50
        }
51
52
        return $this->processRewind(xml: $xml);
53
    }
54
55
    private function processCellXfs(XMLReader $xml): bool
56
    {
57
        if ('cellXfs' === $xml->name) {
58
            switch ($xml->nodeType) {
59
                case XMLReader::END_ELEMENT:
60
                    return true;
61
                case XMLReader::ELEMENT:
62
                    $this->inXfs = true;
63
64
                    return true;
65
            }
66
        }
67
68
        return false;
69
    }
70
71
    private function processRewind(XMLReader $xml): XMLReader
72
    {
73
        if ($this->needsRewind) {
74
            $xml->close();
75
            $xml = parent::createXMLReader();
76
        }
77
78
        return $xml;
79
    }
80
81
    private function xfs(XMLReader $xml): void
82
    {
83
        if ($this->inXfs && XMLReader::ELEMENT === $xml->nodeType && 'xf' === $xml->name) {
84
            $this->values[] = $this->getValue(fmtId: (int) $xml->getAttribute(name: 'numFmtId'));
85
        }
86
    }
87
88
    private function process(XMLReader $xml): void
89
    {
90
        if (XMLReader::ELEMENT === $xml->nodeType) {
91
            match ($xml->name) {
92
                'cellXfs' => $this->needsRewind = true,
93
                'numFmt' => $this->numberFormats[$xml->getAttribute(name: 'numFmtId')] = $this->matchDateFormat(xml: $xml),
94
                default => null,
95
            };
96
        }
97
    }
98
99
    private function matchDateFormat(XMLReader $xml): int
100
    {
101
        return preg_match(pattern: '{^(\[\$[[:alpha:]]*-[0-9A-F]*\])*[hmsdy]}i', subject: $xml->getAttribute(name: 'formatCode')) ? self::FORMAT_DATE : self::FORMAT_DEFAULT;
102
    }
103
104
    private function getValue(int $fmtId): int
105
    {
106
        return match (true) {
107
            in_array(needle: $fmtId, haystack: $this->nativeDateFormats, strict: true) => self::FORMAT_DATE,
108
            isset($this->numberFormats[$fmtId]) => $this->numberFormats[$fmtId],
109
            default => self::FORMAT_DEFAULT,
110
        };
111
    }
112
}
113