Styles::processRewind()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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