Functions   F
last analyzed

Complexity

Total Complexity 62

Size/Duplication

Total Lines 353
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 62
eloc 128
dl 0
loc 353
ccs 125
cts 125
cp 1
rs 3.44
c 0
b 0
f 0

18 Methods

Rating   Name   Duplication   Size   Complexity  
A isCellValue() 0 5 1
C ifCondition() 0 37 14
A isValue() 0 5 1
A scalar() 0 11 3
A trimSheetFromCellReference() 0 7 2
A isMatrixValue() 0 5 2
A flattenArray2() 0 16 3
A setCompatibilityMode() 0 13 4
A getReturnDateType() 0 3 1
A setReturnDateType() 0 13 4
A getCompatibilityMode() 0 3 1
A flattenArray() 0 20 4
B operandSpecialHandling() 0 21 7
A expandDefinedName() 0 18 4
B flattenArrayIndexed() 0 24 7
A trimTrailingRange() 0 3 1
A flattenSingleValue() 0 7 2
A DUMMY() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Functions often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Functions, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation;
4
5
use PhpOffice\PhpSpreadsheet\Cell\Cell;
6
use PhpOffice\PhpSpreadsheet\Shared\Date;
7
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
8
9
class Functions
10
{
11
    const PRECISION = 8.88E-016;
12
13
    /**
14
     * 2 / PI.
15
     */
16
    const M_2DIVPI = 0.63661977236758134307553505349006;
17
18
    const COMPATIBILITY_EXCEL = 'Excel';
19
    const COMPATIBILITY_GNUMERIC = 'Gnumeric';
20
    const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
21
22
    /** Use of RETURNDATE_PHP_NUMERIC is discouraged - not 32-bit Y2038-safe, no timezone. */
23
    const RETURNDATE_PHP_NUMERIC = 'P';
24
    /** Use of RETURNDATE_UNIX_TIMESTAMP is discouraged - not 32-bit Y2038-safe, no timezone. */
25
    const RETURNDATE_UNIX_TIMESTAMP = 'P';
26
    const RETURNDATE_PHP_OBJECT = 'O';
27
    const RETURNDATE_PHP_DATETIME_OBJECT = 'O';
28
    const RETURNDATE_EXCEL = 'E';
29
30
    public const NOT_YET_IMPLEMENTED = '#Not Yet Implemented';
31
32
    /**
33
     * Compatibility mode to use for error checking and responses.
34
     */
35
    protected static string $compatibilityMode = self::COMPATIBILITY_EXCEL;
36
37
    /**
38
     * Data Type to use when returning date values.
39
     */
40
    protected static string $returnDateType = self::RETURNDATE_EXCEL;
41
42
    /**
43
     * Set the Compatibility Mode.
44
     *
45
     * @param string $compatibilityMode Compatibility Mode
46
     *                                  Permitted values are:
47
     *                                      Functions::COMPATIBILITY_EXCEL        'Excel'
48
     *                                      Functions::COMPATIBILITY_GNUMERIC     'Gnumeric'
49
     *                                      Functions::COMPATIBILITY_OPENOFFICE   'OpenOfficeCalc'
50
     *
51
     * @return bool (Success or Failure)
52
     */
53 8861
    public static function setCompatibilityMode(string $compatibilityMode): bool
54
    {
55
        if (
56 8861
            ($compatibilityMode == self::COMPATIBILITY_EXCEL)
57 8861
            || ($compatibilityMode == self::COMPATIBILITY_GNUMERIC)
58 8861
            || ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)
59
        ) {
60 8861
            self::$compatibilityMode = $compatibilityMode;
61
62 8861
            return true;
63
        }
64
65 1
        return false;
66
    }
67
68
    /**
69
     * Return the current Compatibility Mode.
70
     *
71
     * @return string Compatibility Mode
72
     *                Possible Return values are:
73
     *                    Functions::COMPATIBILITY_EXCEL        'Excel'
74
     *                    Functions::COMPATIBILITY_GNUMERIC     'Gnumeric'
75
     *                    Functions::COMPATIBILITY_OPENOFFICE   'OpenOfficeCalc'
76
     */
77 10115
    public static function getCompatibilityMode(): string
78
    {
79 10115
        return self::$compatibilityMode;
80
    }
81
82
    /**
83
     * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP DateTime Object).
84
     *
85
     * @param string $returnDateType Return Date Format
86
     *                               Permitted values are:
87
     *                                   Functions::RETURNDATE_UNIX_TIMESTAMP       'P'
88
     *                                   Functions::RETURNDATE_PHP_DATETIME_OBJECT  'O'
89
     *                                   Functions::RETURNDATE_EXCEL                'E'
90
     *
91
     * @return bool Success or failure
92
     */
93 3261
    public static function setReturnDateType(string $returnDateType): bool
94
    {
95
        if (
96 3261
            ($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP)
97 3261
            || ($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT)
98 3261
            || ($returnDateType == self::RETURNDATE_EXCEL)
99
        ) {
100 3261
            self::$returnDateType = $returnDateType;
101
102 3261
            return true;
103
        }
104
105 1
        return false;
106
    }
107
108
    /**
109
     * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
110
     *
111
     * @return string Return Date Format
112
     *                Possible Return values are:
113
     *                    Functions::RETURNDATE_UNIX_TIMESTAMP         'P'
114
     *                    Functions::RETURNDATE_PHP_DATETIME_OBJECT    'O'
115
     *                    Functions::RETURNDATE_EXCEL            '     'E'
116
     */
117 3381
    public static function getReturnDateType(): string
118
    {
119 3381
        return self::$returnDateType;
120
    }
121
122
    /**
123
     * DUMMY.
124
     *
125
     * @return string #Not Yet Implemented
126
     */
127 15
    public static function DUMMY(): string
128
    {
129 15
        return self::NOT_YET_IMPLEMENTED;
130
    }
131
132 5
    public static function isMatrixValue(mixed $idx): bool
133
    {
134 5
        $idx = StringHelper::convertToString($idx);
135
136 5
        return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
137
    }
138
139 1
    public static function isValue(mixed $idx): bool
140
    {
141 1
        $idx = StringHelper::convertToString($idx);
142
143 1
        return substr_count($idx, '.') === 0;
144
    }
145
146 102
    public static function isCellValue(mixed $idx): bool
147
    {
148 102
        $idx = StringHelper::convertToString($idx);
149
150 102
        return substr_count($idx, '.') > 1;
151
    }
152
153 212
    public static function ifCondition(mixed $condition): string
154
    {
155 212
        $condition = self::flattenSingleValue($condition);
156
157 212
        if ($condition === '' || $condition === null) {
158 3
            return '=""';
159
        }
160 211
        if (!is_string($condition) || !in_array($condition[0], ['>', '<', '='], true)) {
161 121
            $condition = self::operandSpecialHandling($condition);
162 121
            if (is_bool($condition)) {
163 3
                return '=' . ($condition ? 'TRUE' : 'FALSE');
164
            }
165 118
            if (!is_numeric($condition)) {
166 98
                if ($condition !== '""') { // Not an empty string
167
                    // Escape any quotes in the string value
168 97
                    $condition = (string) preg_replace('/"/ui', '""', $condition);
169
                }
170 98
                $condition = Calculation::wrapResult(strtoupper($condition));
171
            }
172
173 118
            return str_replace('""""', '""', '=' . StringHelper::convertToString($condition));
174
        }
175 126
        $operator = $operand = '';
176 126
        if (1 === preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches)) {
177 126
            [, $operator, $operand] = $matches;
178
        }
179
180 126
        $operand = (string) self::operandSpecialHandling($operand);
181 126
        if (is_numeric(trim($operand, '"'))) {
182 73
            $operand = trim($operand, '"');
183 80
        } elseif (!is_numeric($operand) && $operand !== 'FALSE' && $operand !== 'TRUE') {
184 76
            $operand = str_replace('"', '""', $operand);
185 76
            $operand = Calculation::wrapResult(strtoupper($operand));
186 76
            $operand = StringHelper::convertToString($operand);
187
        }
188
189 126
        return str_replace('""""', '""', $operator . $operand);
190
    }
191
192 211
    private static function operandSpecialHandling(mixed $operand): bool|float|int|string
193
    {
194 211
        if (is_numeric($operand) || is_bool($operand)) {
195 111
            return $operand;
196
        }
197 174
        $operand = StringHelper::convertToString($operand);
198 174
        if (strtoupper($operand) === Calculation::getTRUE() || strtoupper($operand) === Calculation::getFALSE()) {
199 4
            return strtoupper($operand);
200
        }
201
202
        // Check for percentage
203 170
        if (preg_match('/^\-?\d*\.?\d*\s?\%$/', $operand)) {
204 4
            return ((float) rtrim($operand, '%')) / 100;
205
        }
206
207
        // Check for dates
208 170
        if (($dateValueOperand = Date::stringToExcel($operand)) !== false) {
209 4
            return $dateValueOperand;
210
        }
211
212 170
        return $operand;
213
    }
214
215
    /**
216
     * Convert a multi-dimensional array to a simple 1-dimensional array.
217
     *
218
     * @param mixed $array Array to be flattened
219
     *
220
     * @return array<mixed> Flattened array
221
     */
222 6060
    public static function flattenArray(mixed $array): array
223
    {
224 6060
        if (!is_array($array)) {
225 24
            return (array) $array;
226
        }
227
228 6050
        $flattened = [];
229 6050
        $stack = array_values($array);
230
231 6050
        while (!empty($stack)) {
232 5945
            $value = array_shift($stack);
233
234 5945
            if (is_array($value)) {
235 2545
                array_unshift($stack, ...array_values($value));
236
            } else {
237 5942
                $flattened[] = $value;
238
            }
239
        }
240
241 6050
        return $flattened;
242
    }
243
244
    /**
245
     * Convert a multi-dimensional array to a simple 1-dimensional array.
246
     * Same as above but argument is specified in ... format.
247
     *
248
     * @param mixed $array Array to be flattened
249
     *
250
     * @return array<mixed> Flattened array
251
     */
252 33
    public static function flattenArray2(mixed ...$array): array
253
    {
254 33
        $flattened = [];
255 33
        $stack = array_values($array);
256
257 33
        while (!empty($stack)) {
258 33
            $value = array_shift($stack);
259
260 33
            if (is_array($value)) {
261 10
                array_unshift($stack, ...array_values($value));
262
            } else {
263 33
                $flattened[] = $value;
264
            }
265
        }
266
267 33
        return $flattened;
268
    }
269
270 724
    public static function scalar(mixed $value): mixed
271
    {
272 724
        if (!is_array($value)) {
273 723
            return $value;
274
        }
275
276
        do {
277 1
            $value = array_pop($value);
278 1
        } while (is_array($value));
279
280 1
        return $value;
281
    }
282
283
    /**
284
     * Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing.
285
     *
286
     * @param array|mixed $array Array to be flattened
287
     *
288
     * @return array<mixed> Flattened array
289
     */
290 763
    public static function flattenArrayIndexed($array): array
291
    {
292 763
        if (!is_array($array)) {
293 12
            return (array) $array;
294
        }
295
296 752
        $arrayValues = [];
297 752
        foreach ($array as $k1 => $value) {
298 752
            if (is_array($value)) {
299 554
                foreach ($value as $k2 => $val) {
300 550
                    if (is_array($val)) {
301 502
                        foreach ($val as $k3 => $v) {
302 495
                            $arrayValues[$k1 . '.' . $k2 . '.' . $k3] = $v;
303
                        }
304
                    } else {
305 65
                        $arrayValues[$k1 . '.' . $k2] = $val;
306
                    }
307
                }
308
            } else {
309 314
                $arrayValues[$k1] = $value;
310
            }
311
        }
312
313 752
        return $arrayValues;
314
    }
315
316
    /**
317
     * Convert an array to a single scalar value by extracting the first element.
318
     *
319
     * @param mixed $value Array or scalar value
320
     */
321 12563
    public static function flattenSingleValue(mixed $value): mixed
322
    {
323 12563
        while (is_array($value)) {
324 7375
            $value = array_shift($value);
325
        }
326
327 12563
        return $value;
328
    }
329
330 12
    public static function expandDefinedName(string $coordinate, Cell $cell): string
331
    {
332 12
        $worksheet = $cell->getWorksheet();
333 12
        $spreadsheet = $worksheet->getParentOrThrow();
334
        // Uppercase coordinate
335 12
        $pCoordinatex = strtoupper($coordinate);
336
        // Eliminate leading equal sign
337 12
        $pCoordinatex = (string) preg_replace('/^=/', '', $pCoordinatex);
338 12
        $defined = $spreadsheet->getDefinedName($pCoordinatex, $worksheet);
339 12
        if ($defined !== null) {
340 1
            $worksheet2 = $defined->getWorkSheet();
341 1
            if (!$defined->isFormula() && $worksheet2 !== null) {
342 1
                $coordinate = "'" . $worksheet2->getTitle() . "'!"
343 1
                    . (string) preg_replace('/^=/', '', str_replace('$', '', $defined->getValue()));
344
            }
345
        }
346
347 12
        return $coordinate;
348
    }
349
350 18
    public static function trimTrailingRange(string $coordinate): string
351
    {
352 18
        return (string) preg_replace('/:[\w\$]+$/', '', $coordinate);
353
    }
354
355 10253
    public static function trimSheetFromCellReference(string $coordinate): string
356
    {
357 10253
        if (str_contains($coordinate, '!')) {
358 12
            $coordinate = substr($coordinate, strrpos($coordinate, '!') + 1);
359
        }
360
361 10253
        return $coordinate;
362
    }
363
}
364