Completed
Pull Request — master (#222)
by Hura
04:43 queued 02:06
created

CellValueFormatter::formatStringCellValue()   C

Complexity

Conditions 7
Paths 7

Size

Total Lines 27
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 7

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 27
ccs 21
cts 21
cp 1
rs 6.7272
cc 7
eloc 18
nc 7
nop 1
crap 7
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
     */
43 66
    public function __construct()
44
    {
45
        /** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
46 66
        $this->escaper = new \Box\Spout\Common\Escaper\ODS();
47 66
    }
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
     */
56 66
    public function extractAndFormatNodeValue($node)
57
    {
58 66
        $cellType = $node->getAttribute(self::XML_ATTRIBUTE_TYPE);
59
60
        switch ($cellType) {
61 66
            case self::CELL_TYPE_STRING:
62 60
                return $this->formatStringCellValue($node);
63 36
            case self::CELL_TYPE_FLOAT:
64 21
                return $this->formatFloatCellValue($node);
65 30
            case self::CELL_TYPE_BOOLEAN:
66 6
                return $this->formatBooleanCellValue($node);
67 30
            case self::CELL_TYPE_DATE:
68 6
                return $this->formatDateCellValue($node);
69 30
            case self::CELL_TYPE_TIME:
70 6
                return $this->formatTimeCellValue($node);
71 27
            case self::CELL_TYPE_CURRENCY:
72 3
                return $this->formatCurrencyCellValue($node);
73 27
            case self::CELL_TYPE_PERCENTAGE:
74 3
                return $this->formatPercentageCellValue($node);
75 27
            case self::CELL_TYPE_VOID:
76 27
            default:
77 27
                return '';
78 27
        }
79
    }
80
81
    /**
82
     * Returns the cell String value.
83
     *
84
     * @param \DOMNode $node
85
     * @return string The value associated with the cell
86
     */
87 60
    protected function formatStringCellValue($node)
88
    {
89 60
        $pNodeValues = [];
90 60
        $pNodes = $node->getElementsByTagName(self::XML_NODE_P);
91
92 60
        foreach ($pNodes as $pNode) {
93 60
            $currentPValue = '';
94
95 60
            foreach ($pNode->childNodes as $childNode) {
96 60
                if ($childNode instanceof \DOMText) {
97 60
                    $currentPValue .= $childNode->nodeValue;
98 60
                } 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 9
                } else if ($childNode->nodeName === self::XML_NODE_A) {
103 3
                    $currentPValue .= $childNode->nodeValue;
104 3
                }
105 60
            }
106
107 60
            $pNodeValues[] = $currentPValue;
108 60
        }
109
110 60
        $escapedCellValue = implode("\n", $pNodeValues);
111 60
        $cellValue = $this->escaper->unescape($escapedCellValue);
112 60
        return $cellValue;
113
    }
114
115
    /**
116
     * Returns the cell Numeric value from the given node.
117
     *
118
     * @param \DOMNode $node
119
     * @return int|float The value associated with the cell
120
     */
121 21
    protected function formatFloatCellValue($node)
122
    {
123 21
        $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_VALUE);
124 21
        $nodeIntValue = intval($nodeValue);
125 21
        $cellValue = ($nodeIntValue == $nodeValue) ? $nodeIntValue : floatval($nodeValue);
126 21
        return $cellValue;
127
    }
128
129
    /**
130
     * Returns the cell Boolean value from the given node.
131
     *
132
     * @param \DOMNode $node
133
     * @return bool The value associated with the cell
134
     */
135 6
    protected function formatBooleanCellValue($node)
136
    {
137 6
        $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_BOOLEAN_VALUE);
138
        // !! is similar to boolval()
139 6
        $cellValue = !!$nodeValue;
140 6
        return $cellValue;
141
    }
142
143
    /**
144
     * Returns the cell Date value from the given node.
145
     *
146
     * @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
    {
151
        try {
152 6
            $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_DATE_VALUE);
153 6
            return new \DateTime($nodeValue);
154 3
        } catch (\Exception $e) {
155 3
            return null;
156
        }
157
    }
158
159
    /**
160
     * Returns the cell Time value from the given node.
161
     *
162
     * @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
    {
167
        try {
168 6
            $nodeValue = $node->getAttribute(self::XML_ATTRIBUTE_TIME_VALUE);
169 6
            return new \DateInterval($nodeValue);
170 3
        } catch (\Exception $e) {
171 3
            return null;
172
        }
173
    }
174
175
    /**
176
     * Returns the cell Currency value from the given node.
177
     *
178
     * @param \DOMNode $node
179
     * @return string The value associated with the cell (e.g. "100 USD" or "9.99 EUR")
180
     */
181 3
    protected function formatCurrencyCellValue($node)
182
    {
183 3
        $value = $node->getAttribute(self::XML_ATTRIBUTE_VALUE);
184 3
        $currency = $node->getAttribute(self::XML_ATTRIBUTE_CURRENCY);
185
186 3
        return "$value $currency";
187
    }
188
189
    /**
190
     * Returns the cell Percentage value from the given node.
191
     *
192
     * @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 3
        return $this->formatFloatCellValue($node);
199
    }
200
}
201