Completed
Pull Request — master (#222)
by Hura
11:45
created

CellValueFormatter::formatTimeCellValue()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
ccs 4
cts 4
cp 1
rs 9.6666
cc 2
eloc 6
nc 3
nop 1
crap 2
1
<?php
2
3
namespace Box\Spout\Reader\ODS\Helper;
4
5
/**
6
 * Class CellValueFormatter
7
 * This class provides helper functions to format cell values
8
 *
9
 * @package Box\Spout\Reader\ODS\Helper
10
 */
11
class CellValueFormatter
12
{
13
    /** Definition of all possible cell types */
14
    const CELL_TYPE_STRING = 'string';
15
    const CELL_TYPE_FLOAT = 'float';
16
    const CELL_TYPE_BOOLEAN = 'boolean';
17
    const CELL_TYPE_DATE = 'date';
18
    const CELL_TYPE_TIME = 'time';
19
    const CELL_TYPE_CURRENCY = 'currency';
20
    const CELL_TYPE_PERCENTAGE = 'percentage';
21
    const CELL_TYPE_VOID = 'void';
22
23
    /** Definition of XML nodes names used to parse data */
24
    const XML_NODE_P = 'p';
25
    const XML_NODE_S = 'text:s';
26
    const XML_NODE_A = 'text:a';
27
28
    /** Definition of XML attribute used to parse data */
29
    const XML_ATTRIBUTE_TYPE = 'office:value-type';
30
    const XML_ATTRIBUTE_VALUE = 'office:value';
31
    const XML_ATTRIBUTE_BOOLEAN_VALUE = 'office:boolean-value';
32
    const XML_ATTRIBUTE_DATE_VALUE = 'office:date-value';
33
    const XML_ATTRIBUTE_TIME_VALUE = 'office:time-value';
34
    const XML_ATTRIBUTE_CURRENCY = 'office:currency';
35
    const XML_ATTRIBUTE_C = 'text:c';
36
37
    /** @var \Box\Spout\Common\Escaper\ODS Used to unescape XML data */
38
    protected $escaper;
39
40
    /**
41
     *
42 63
     */
43
    public function __construct()
44
    {
45 63
        /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
46 63
        $this->escaper = new \Box\Spout\Common\Escaper\ODS();
47
    }
48
49
    /**
50
     * Returns the (unescaped) correctly marshalled, cell value associated to the given XML node.
51
     * @see http://docs.oasis-open.org/office/v1.2/os/OpenDocument-v1.2-os-part1.html#refTable13
52
     *
53
     * @param \DOMNode $node
54
     * @return string|int|float|bool|\DateTime|\DateInterval|null The value associated with the cell, empty string if cell's type is void/undefined, null on error
55 63
     */
56
    public function extractAndFormatNodeValue($node)
57 63
    {
58
        $cellType = $node->getAttribute(self::XML_ATTRIBUTE_TYPE);
59
60 63
        switch ($cellType) {
61 57
            case self::CELL_TYPE_STRING:
62 36
                return $this->formatStringCellValue($node);
63 21
            case self::CELL_TYPE_FLOAT:
64 30
                return $this->formatFloatCellValue($node);
65 6
            case self::CELL_TYPE_BOOLEAN:
66 30
                return $this->formatBooleanCellValue($node);
67 6
            case self::CELL_TYPE_DATE:
68 30
                return $this->formatDateCellValue($node);
69 6
            case self::CELL_TYPE_TIME:
70 27
                return $this->formatTimeCellValue($node);
71 3
            case self::CELL_TYPE_CURRENCY:
72 27
                return $this->formatCurrencyCellValue($node);
73 3
            case self::CELL_TYPE_PERCENTAGE:
74 27
                return $this->formatPercentageCellValue($node);
75 27
            case self::CELL_TYPE_VOID:
76 27
            default:
77 27
                return '';
78
        }
79
    }
80
81
    /**
82
     * Returns the cell String value.
83
     *
84
     * @param \DOMNode $node
85
     * @return string The value associated with the cell
86 57
     */
87
    protected function formatStringCellValue($node)
88 57
    {
89 57
        $pNodeValues = [];
90
        $pNodes = $node->getElementsByTagName(self::XML_NODE_P);
91 57
92 57
        foreach ($pNodes as $pNode) {
93
            $currentPValue = '';
94 57
95 57
            foreach ($pNode->childNodes as $childNode) {
96 57
                if ($childNode instanceof \DOMText) {
97 57
                    $currentPValue .= $childNode->nodeValue;
98 3
                } else if ($childNode->nodeName === self::XML_NODE_S) {
99 3
                    $spaceAttribute = $childNode->getAttribute(self::XML_ATTRIBUTE_C);
100 3
                    $numSpaces = (!empty($spaceAttribute)) ? intval($spaceAttribute) : 1;
101 3
                    $currentPValue .= str_repeat(' ', $numSpaces);
102 57
                } else if ($childNode->nodeName === self::XML_NODE_A) {
103
                    $currentPValue .= $childNode->nodeValue;
104 57
                }
105 57
            }
106
107 57
            $pNodeValues[] = $currentPValue;
108 57
        }
109 57
110
        $escapedCellValue = implode("\n", $pNodeValues);
111
        $cellValue = $this->escaper->unescape($escapedCellValue);
112
        return $cellValue;
113
    }
114
115
    /**
116
     * Returns the cell Numeric value from the given node.
117
     *
118 21
     * @param \DOMNode $node
119
     * @return int|float The value associated with the cell
120 21
     */
121 21
    protected function formatFloatCellValue($node)
122 21
    {
123 21
        $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_VALUE);
124
        $nodeIntValue = intval($nodeValue);
125
        $cellValue = ($nodeIntValue == $nodeValue) ? $nodeIntValue : floatval($nodeValue);
126
        return $cellValue;
127
    }
128
129
    /**
130
     * Returns the cell Boolean value from the given node.
131
     *
132 6
     * @param \DOMNode $node
133
     * @return bool The value associated with the cell
134 6
     */
135
    protected function formatBooleanCellValue($node)
136 6
    {
137 6
        $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_BOOLEAN_VALUE);
138
        // !! is similar to boolval()
139
        $cellValue = !!$nodeValue;
140
        return $cellValue;
141
    }
142
143
    /**
144
     * Returns the cell Date value from the given node.
145
     *
146 6
     * @param \DOMNode $node
147
     * @return \DateTime|null The value associated with the cell or NULL if invalid date value
148
     */
149 6
    protected function formatDateCellValue($node)
150 6
    {
151 3
        try {
152 3
            $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_DATE_VALUE);
153
            return new \DateTime($nodeValue);
154
        } catch (\Exception $e) {
155
            return null;
156
        }
157
    }
158
159
    /**
160
     * Returns the cell Time value from the given node.
161
     *
162 6
     * @param \DOMNode $node
163
     * @return \DateInterval|null The value associated with the cell or NULL if invalid time value
164
     */
165 6
    protected function formatTimeCellValue($node)
166 6
    {
167 3
        try {
168 3
            $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_TIME_VALUE);
169
            return new \DateInterval($nodeValue);
170
        } catch (\Exception $e) {
171
            return null;
172
        }
173
    }
174
175
    /**
176
     * Returns the cell Currency value from the given node.
177
     *
178 3
     * @param \DOMNode $node
179
     * @return string The value associated with the cell (e.g. "100 USD" or "9.99 EUR")
180 3
     */
181 3
    protected function formatCurrencyCellValue($node)
182
    {
183 3
        $value = $node->getAttribute(self::XML_ATTRIBUTE_VALUE);
184
        $currency = $node->getAttribute(self::XML_ATTRIBUTE_CURRENCY);
185
186
        return "$value $currency";
187
    }
188
189
    /**
190
     * Returns the cell Percentage value from the given node.
191
     *
192 3
     * @param \DOMNode $node
193
     * @return int|float The value associated with the cell
194
     */
195 3
    protected function formatPercentageCellValue($node)
196
    {
197
        // percentages are formatted like floats
198
        return $this->formatFloatCellValue($node);
199
    }
200
}
201