Passed
Push — master ( d49bc4...5072ff )
by Mark
13:27
created

AdvancedValueBinder::bindValue()   C

Complexity

Conditions 16
Paths 27

Size

Total Lines 89
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 45
CRAP Score 16.0197

Importance

Changes 0
Metric Value
eloc 44
c 0
b 0
f 0
dl 0
loc 89
ccs 45
cts 47
cp 0.9574
rs 5.5666
cc 16
nc 27
nop 2
crap 16.0197

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Cell;
4
5
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
6
use PhpOffice\PhpSpreadsheet\Calculation\Engine\FormattedNumber;
7
use PhpOffice\PhpSpreadsheet\RichText\RichText;
8
use PhpOffice\PhpSpreadsheet\Shared\Date;
9
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
10
use PhpOffice\PhpSpreadsheet\Style\NumberFormat;
11
12
class AdvancedValueBinder extends DefaultValueBinder implements IValueBinder
13
{
14
    /**
15
     * Bind value to a cell.
16
     *
17
     * @param Cell $cell Cell to bind value to
18
     * @param mixed $value Value to bind in cell
19
     *
20
     * @return bool
21 35
     */
22
    public function bindValue(Cell $cell, $value = null)
23 35
    {
24 1
        if ($value === null) {
25 34
            return parent::bindValue($cell, $value);
26
        } elseif (is_string($value)) {
27 34
            // sanitize UTF-8 strings
28
            $value = StringHelper::sanitizeUTF8($value);
29
        }
30
31 34
        // Find out data type
32
        $dataType = parent::dataTypeForValue($value);
33
34 34
        // Style logic - strings
35
        if ($dataType === DataType::TYPE_STRING && !$value instanceof RichText) {
36 34
            //    Test for booleans using locale-setting
37 1
            if ($value == Calculation::getTRUE()) {
38
                $cell->setValueExplicit(true, DataType::TYPE_BOOL);
39 1
40 34
                return true;
41
            } elseif ($value == Calculation::getFALSE()) {
42
                $cell->setValueExplicit(false, DataType::TYPE_BOOL);
43
44
                return true;
45
            }
46
47 34
            // Check for fractions
48 7
            if (preg_match('/^([+-]?)\s*(\d+)\s?\/\s*(\d+)$/', $value, $matches)) {
49 28
                return $this->setProperFraction($matches, $cell);
50 6
            } elseif (preg_match('/^([+-]?)(\d*) +(\d*)\s?\/\s*(\d*)$/', $value, $matches)) {
51
                return $this->setImproperFraction($matches, $cell);
52
            }
53
54 23
            $decimalSeparator = preg_quote(StringHelper::getDecimalSeparator());
55 8
            $thousandsSeparator = preg_quote(StringHelper::getThousandsSeparator());
56
57
            // Check for percentage
58
            if (preg_match('/^\-?\d*' . $decimalSeparator . '?\d*\s?\%$/', preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value))) {
59 17
                return $this->setPercentage(preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value), $cell);
60 17
            }
61 17
62 17
            // Check for currency
63
            if (preg_match(FormattedNumber::currencyMatcherRegexp(), preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value), $matches, PREG_UNMATCHED_AS_NULL)) {
64 8
                // Convert value to number
65 8
                $sign = ($matches['PrefixedSign'] ?? $matches['PrefixedSign2'] ?? $matches['PostfixedSign']) ?? null;
66
                $currencyCode = $matches['PrefixedCurrency'] ?? $matches['PostfixedCurrency'];
67 8
                $value = (float) ($sign . trim(str_replace([$decimalSeparator, $currencyCode, ' ', '-'], ['.', '', '', ''], preg_replace('/(\d)' . $thousandsSeparator . '(\d)/u', '$1$2', $value)))); // @phpstan-ignore-line
68 8
69 8
                return $this->setCurrency($value, $cell, $currencyCode); // @phpstan-ignore-line
70 8
            }
71
72 8
            // Check for time without seconds e.g. '9:45', '09:45'
73 10
            if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d$/', $value)) {
74
                return $this->setTimeHoursMinutes($value, $cell);
75 1
            }
76 1
77
            // Check for time with seconds '9:45:59', '09:45:59'
78 1
            if (preg_match('/^(\d|[0-1]\d|2[0-3]):[0-5]\d:[0-5]\d$/', $value)) {
79 1
                return $this->setTimeHoursMinutesSeconds($value, $cell);
80
            }
81 1
82
            // Check for datetime, e.g. '2008-12-31', '2008-12-31 15:59', '2008-12-31 15:59:10'
83
            if (($d = Date::stringToExcel($value)) !== false) {
84
                // Convert value to number
85 9
                $cell->setValueExplicit($d, DataType::TYPE_NUMERIC);
86 5
                // Determine style. Either there is a time part or not. Look for ':'
87
                if (strpos($value, ':') !== false) {
88
                    $formatCode = 'yyyy-mm-dd h:mm';
89
                } else {
90 6
                    $formatCode = 'yyyy-mm-dd';
91 3
                }
92
                $cell->getWorksheet()->getStyle($cell->getCoordinate())
93
                    ->getNumberFormat()->setFormatCode($formatCode);
94
95 4
                return true;
96
            }
97 2
98
            // Check for newline character "\n"
99 2
            if (strpos($value, "\n") !== false) {
100 1
                $cell->setValueExplicit($value, DataType::TYPE_STRING);
101
                // Set style
102 2
                $cell->getWorksheet()->getStyle($cell->getCoordinate())
103
                    ->getAlignment()->setWrapText(true);
104 2
105 2
                return true;
106
            }
107 2
        }
108
109
        // Not bound yet? Use parent...
110
        return parent::bindValue($cell, $value);
111 4
    }
112 1
113
    protected function setImproperFraction(array $matches, Cell $cell): bool
114 1
    {
115 1
        // Convert value to number
116
        $value = $matches[2] + ($matches[3] / $matches[4]);
117 1
        if ($matches[1] === '-') {
118
            $value = 0 - $value;
119
        }
120
        $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
121
122 3
        // Build the number format mask based on the size of the matched values
123
        $dividend = str_repeat('?', strlen($matches[3]));
124
        $divisor = str_repeat('?', strlen($matches[4]));
125 6
        $fractionMask = "# {$dividend}/{$divisor}";
126
        // Set style
127
        $cell->getWorksheet()->getStyle($cell->getCoordinate())
128 6
            ->getNumberFormat()->setFormatCode($fractionMask);
129 6
130 2
        return true;
131
    }
132 6
133
    protected function setProperFraction(array $matches, Cell $cell): bool
134
    {
135 6
        // Convert value to number
136 6
        $value = $matches[2] / $matches[3];
137 6
        if ($matches[1] === '-') {
138
            $value = 0 - $value;
139 6
        }
140 6
        $cell->setValueExplicit((float) $value, DataType::TYPE_NUMERIC);
141
142 6
        // Build the number format mask based on the size of the matched values
143
        $dividend = str_repeat('?', strlen($matches[2]));
144
        $divisor = str_repeat('?', strlen($matches[3]));
145 7
        $fractionMask = "{$dividend}/{$divisor}";
146
        // Set style
147
        $cell->getWorksheet()->getStyle($cell->getCoordinate())
148 7
            ->getNumberFormat()->setFormatCode($fractionMask);
149 7
150 2
        return true;
151
    }
152 7
153
    protected function setPercentage(string $value, Cell $cell): bool
154
    {
155 7
        // Convert value to number
156 7
        $value = ((float) str_replace('%', '', $value)) / 100;
157 7
        $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
158
159 7
        // Set style
160 7
        $cell->getWorksheet()->getStyle($cell->getCoordinate())
161
            ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_PERCENTAGE_00);
162 7
163
        return true;
164
    }
165 8
166
    protected function setCurrency(float $value, Cell $cell, string $currencyCode): bool
167
    {
168 8
        $cell->setValueExplicit($value, DataType::TYPE_NUMERIC);
169 8
        // Set style
170
        $cell->getWorksheet()->getStyle($cell->getCoordinate())
171
            ->getNumberFormat()->setFormatCode(
172 8
                str_replace('$', '[$' . $currencyCode . ']', NumberFormat::FORMAT_CURRENCY_USD_SIMPLE)
173 8
            );
174
175 8
        return true;
176
    }
177
178 5
    protected function setTimeHoursMinutes(string $value, Cell $cell): bool
179
    {
180
        // Convert value to number
181 5
        [$hours, $minutes] = explode(':', $value);
182 5
        $hours = (int) $hours;
183 5
        $minutes = (int) $minutes;
184 5
        $days = ($hours / 24) + ($minutes / 1440);
185 5
        $cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
186
187
        // Set style
188 5
        $cell->getWorksheet()->getStyle($cell->getCoordinate())
189 5
            ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_TIME3);
190
191 5
        return true;
192
    }
193
194 3
    protected function setTimeHoursMinutesSeconds(string $value, Cell $cell): bool
195
    {
196
        // Convert value to number
197 3
        [$hours, $minutes, $seconds] = explode(':', $value);
198 3
        $hours = (int) $hours;
199 3
        $minutes = (int) $minutes;
200 3
        $seconds = (int) $seconds;
201 3
        $days = ($hours / 24) + ($minutes / 1440) + ($seconds / 86400);
202 3
        $cell->setValueExplicit($days, DataType::TYPE_NUMERIC);
203
204
        // Set style
205 3
        $cell->getWorksheet()->getStyle($cell->getCoordinate())
206 3
            ->getNumberFormat()->setFormatCode(NumberFormat::FORMAT_DATE_TIME4);
207
208 3
        return true;
209
    }
210
}
211