Passed
Push — master ( a97294...8972d3 )
by Adrien
28:45 queued 21:13
created

Functions::isError()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 1
dl 0
loc 9
ccs 5
cts 5
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation;
4
5
use PhpOffice\PhpSpreadsheet\Cell\Cell;
6
7
class Functions
8
{
9
    const PRECISION = 8.88E-016;
10
11
    /**
12
     * 2 / PI.
13
     */
14
    const M_2DIVPI = 0.63661977236758134307553505349006;
15
16
    /** constants */
17
    const COMPATIBILITY_EXCEL = 'Excel';
18
    const COMPATIBILITY_GNUMERIC = 'Gnumeric';
19
    const COMPATIBILITY_OPENOFFICE = 'OpenOfficeCalc';
20
21
    const RETURNDATE_PHP_NUMERIC = 'P';
22
    const RETURNDATE_UNIX_TIMESTAMP = 'P';
23
    const RETURNDATE_PHP_OBJECT = 'O';
24
    const RETURNDATE_PHP_DATETIME_OBJECT = 'O';
25
    const RETURNDATE_EXCEL = 'E';
26
27
    /**
28
     * Compatibility mode to use for error checking and responses.
29
     *
30
     * @var string
31
     */
32
    protected static $compatibilityMode = self::COMPATIBILITY_EXCEL;
33
34
    /**
35
     * Data Type to use when returning date values.
36
     *
37
     * @var string
38
     */
39
    protected static $returnDateType = self::RETURNDATE_EXCEL;
40
41
    /**
42
     * List of error codes.
43
     *
44
     * @var array
45
     */
46
    protected static $errorCodes = [
47
        'null' => '#NULL!',
48
        'divisionbyzero' => '#DIV/0!',
49
        'value' => '#VALUE!',
50
        'reference' => '#REF!',
51
        'name' => '#NAME?',
52
        'num' => '#NUM!',
53
        'na' => '#N/A',
54
        'gettingdata' => '#GETTING_DATA',
55
    ];
56
57
    /**
58
     * Set the Compatibility Mode.
59
     *
60
     * @category Function Configuration
61
     *
62
     * @param string $compatibilityMode Compatibility Mode
63
     *                                                Permitted values are:
64
     *                                                    Functions::COMPATIBILITY_EXCEL            'Excel'
65
     *                                                    Functions::COMPATIBILITY_GNUMERIC        'Gnumeric'
66
     *                                                    Functions::COMPATIBILITY_OPENOFFICE    'OpenOfficeCalc'
67
     *
68
     * @return bool (Success or Failure)
69
     */
70 4501
    public static function setCompatibilityMode($compatibilityMode)
71
    {
72 4501
        if (($compatibilityMode == self::COMPATIBILITY_EXCEL) ||
73 70
            ($compatibilityMode == self::COMPATIBILITY_GNUMERIC) ||
74 4501
            ($compatibilityMode == self::COMPATIBILITY_OPENOFFICE)
75
        ) {
76 4501
            self::$compatibilityMode = $compatibilityMode;
77
78 4501
            return true;
79
        }
80
81 1
        return false;
82
    }
83
84
    /**
85
     * Return the current Compatibility Mode.
86
     *
87
     * @category Function Configuration
88
     *
89
     * @return string Compatibility Mode
90
     *                            Possible Return values are:
91
     *                                Functions::COMPATIBILITY_EXCEL            'Excel'
92
     *                                Functions::COMPATIBILITY_GNUMERIC        'Gnumeric'
93
     *                                Functions::COMPATIBILITY_OPENOFFICE    'OpenOfficeCalc'
94
     */
95 937
    public static function getCompatibilityMode()
96
    {
97 937
        return self::$compatibilityMode;
98
    }
99
100
    /**
101
     * Set the Return Date Format used by functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
102
     *
103
     * @category Function Configuration
104
     *
105
     * @param string $returnDateType Return Date Format
106
     *                                                Permitted values are:
107
     *                                                    Functions::RETURNDATE_UNIX_TIMESTAMP        'P'
108
     *                                                    Functions::RETURNDATE_PHP_DATETIME_OBJECT        'O'
109
     *                                                    Functions::RETURNDATE_EXCEL            'E'
110
     *
111
     * @return bool Success or failure
112
     */
113 1013
    public static function setReturnDateType($returnDateType)
114
    {
115 1013
        if (($returnDateType == self::RETURNDATE_UNIX_TIMESTAMP) ||
116 1013
            ($returnDateType == self::RETURNDATE_PHP_DATETIME_OBJECT) ||
117 1013
            ($returnDateType == self::RETURNDATE_EXCEL)
118
        ) {
119 1013
            self::$returnDateType = $returnDateType;
120
121 1013
            return true;
122
        }
123
124 1
        return false;
125
    }
126
127
    /**
128
     * Return the current Return Date Format for functions that return a date/time (Excel, PHP Serialized Numeric or PHP Object).
129
     *
130
     * @category Function Configuration
131
     *
132
     * @return string Return Date Format
133
     *                            Possible Return values are:
134
     *                                Functions::RETURNDATE_UNIX_TIMESTAMP        'P'
135
     *                                Functions::RETURNDATE_PHP_DATETIME_OBJECT        'O'
136
     *                                Functions::RETURNDATE_EXCEL            'E'
137
     */
138 678
    public static function getReturnDateType()
139
    {
140 678
        return self::$returnDateType;
141
    }
142
143
    /**
144
     * DUMMY.
145
     *
146
     * @category Error Returns
147
     *
148
     * @return string #Not Yet Implemented
149
     */
150 1
    public static function DUMMY()
151
    {
152 1
        return '#Not Yet Implemented';
153
    }
154
155
    /**
156
     * DIV0.
157
     *
158
     * @category Error Returns
159
     *
160
     * @return string #Not Yet Implemented
161
     */
162 56
    public static function DIV0()
163
    {
164 56
        return self::$errorCodes['divisionbyzero'];
165
    }
166
167
    /**
168
     * NA.
169
     *
170
     * Excel Function:
171
     *        =NA()
172
     *
173
     * Returns the error value #N/A
174
     *        #N/A is the error value that means "no value is available."
175
     *
176
     * @category Logical Functions
177
     *
178
     * @return string #N/A!
179
     */
180 63
    public static function NA()
181
    {
182 63
        return self::$errorCodes['na'];
183
    }
184
185
    /**
186
     * NaN.
187
     *
188
     * Returns the error value #NUM!
189
     *
190
     * @category Error Returns
191
     *
192
     * @return string #NUM!
193
     */
194 183
    public static function NAN()
195
    {
196 183
        return self::$errorCodes['num'];
197
    }
198
199
    /**
200
     * NAME.
201
     *
202
     * Returns the error value #NAME?
203
     *
204
     * @category Error Returns
205
     *
206
     * @return string #NAME?
207
     */
208 5
    public static function NAME()
209
    {
210 5
        return self::$errorCodes['name'];
211
    }
212
213
    /**
214
     * REF.
215
     *
216
     * Returns the error value #REF!
217
     *
218
     * @category Error Returns
219
     *
220
     * @return string #REF!
221
     */
222 5
    public static function REF()
223
    {
224 5
        return self::$errorCodes['reference'];
225
    }
226
227
    /**
228
     * NULL.
229
     *
230
     * Returns the error value #NULL!
231
     *
232
     * @category Error Returns
233
     *
234
     * @return string #NULL!
235
     */
236 1
    public static function null()
237
    {
238 1
        return self::$errorCodes['null'];
239
    }
240
241
    /**
242
     * VALUE.
243
     *
244
     * Returns the error value #VALUE!
245
     *
246
     * @category Error Returns
247
     *
248
     * @return string #VALUE!
249
     */
250 232
    public static function VALUE()
251
    {
252 232
        return self::$errorCodes['value'];
253
    }
254
255 6
    public static function isMatrixValue($idx)
256
    {
257 6
        return (substr_count($idx, '.') <= 1) || (preg_match('/\.[A-Z]/', $idx) > 0);
258
    }
259
260 1
    public static function isValue($idx)
261
    {
262 1
        return substr_count($idx, '.') == 0;
263
    }
264
265 19
    public static function isCellValue($idx)
266
    {
267 19
        return substr_count($idx, '.') > 1;
268
    }
269
270 53
    public static function ifCondition($condition)
271
    {
272 53
        $condition = self::flattenSingleValue($condition);
273 53
        if (!isset($condition[0]) && !is_numeric($condition)) {
274
            $condition = '=""';
275
        }
276 53
        if (!in_array($condition[0], ['>', '<', '='])) {
277 10
            if (!is_numeric($condition)) {
278 4
                $condition = Calculation::wrapResult(strtoupper($condition));
279
            }
280
281 10
            return '=' . $condition;
282
        }
283 45
        preg_match('/(=|<[>=]?|>=?)(.*)/', $condition, $matches);
284 45
        [, $operator, $operand] = $matches;
285
286 45
        if (is_numeric(trim($operand, '"'))) {
287 17
            $operand = trim($operand, '"');
288 32
        } elseif (!is_numeric($operand)) {
289 32
            $operand = str_replace('"', '""', $operand);
290 32
            $operand = Calculation::wrapResult(strtoupper($operand));
291
        }
292
293 45
        return $operator . $operand;
294
    }
295
296
    /**
297
     * ERROR_TYPE.
298
     *
299
     * @param mixed $value Value to check
300
     *
301
     * @return bool
302
     */
303 14
    public static function errorType($value = '')
304
    {
305 14
        $value = self::flattenSingleValue($value);
306
307 14
        $i = 1;
308 14
        foreach (self::$errorCodes as $errorCode) {
309 14
            if ($value === $errorCode) {
310 7
                return $i;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $i returns the type integer which is incompatible with the documented return type boolean.
Loading history...
311
            }
312 13
            ++$i;
313
        }
314
315 7
        return self::NA();
0 ignored issues
show
Bug Best Practice introduced by
The expression return self::NA() returns the type string which is incompatible with the documented return type boolean.
Loading history...
316
    }
317
318
    /**
319
     * IS_BLANK.
320
     *
321
     * @param mixed $value Value to check
322
     *
323
     * @return bool
324
     */
325 16
    public static function isBlank($value = null)
326
    {
327 16
        if ($value !== null) {
328 14
            $value = self::flattenSingleValue($value);
329
        }
330
331 16
        return $value === null;
332
    }
333
334
    /**
335
     * IS_ERR.
336
     *
337
     * @param mixed $value Value to check
338
     *
339
     * @return bool
340
     */
341 16
    public static function isErr($value = '')
342
    {
343 16
        $value = self::flattenSingleValue($value);
344
345 16
        return self::isError($value) && (!self::isNa(($value)));
346
    }
347
348
    /**
349
     * IS_ERROR.
350
     *
351
     * @param mixed $value Value to check
352
     *
353
     * @return bool
354
     */
355 56
    public static function isError($value = '')
356
    {
357 56
        $value = self::flattenSingleValue($value);
358
359 56
        if (!is_string($value)) {
360 16
            return false;
361
        }
362
363 40
        return in_array($value, self::$errorCodes);
364
    }
365
366
    /**
367
     * IS_NA.
368
     *
369
     * @param mixed $value Value to check
370
     *
371
     * @return bool
372
     */
373 20
    public static function isNa($value = '')
374
    {
375 20
        $value = self::flattenSingleValue($value);
376
377 20
        return $value === self::NA();
378
    }
379
380
    /**
381
     * IS_EVEN.
382
     *
383
     * @param mixed $value Value to check
384
     *
385
     * @return bool|string
386
     */
387 20
    public static function isEven($value = null)
388
    {
389 20
        $value = self::flattenSingleValue($value);
390
391 20
        if ($value === null) {
392 2
            return self::NAME();
393 18
        } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
394 7
            return self::VALUE();
395
        }
396
397 11
        return $value % 2 == 0;
398
    }
399
400
    /**
401
     * IS_ODD.
402
     *
403
     * @param mixed $value Value to check
404
     *
405
     * @return bool|string
406
     */
407 20
    public static function isOdd($value = null)
408
    {
409 20
        $value = self::flattenSingleValue($value);
410
411 20
        if ($value === null) {
412 2
            return self::NAME();
413 18
        } elseif ((is_bool($value)) || ((is_string($value)) && (!is_numeric($value)))) {
414 7
            return self::VALUE();
415
        }
416
417 11
        return abs($value) % 2 == 1;
418
    }
419
420
    /**
421
     * IS_NUMBER.
422
     *
423
     * @param mixed $value Value to check
424
     *
425
     * @return bool
426
     */
427 16
    public static function isNumber($value = null)
428
    {
429 16
        $value = self::flattenSingleValue($value);
430
431 16
        if (is_string($value)) {
432 8
            return false;
433
        }
434
435 8
        return is_numeric($value);
436
    }
437
438
    /**
439
     * IS_LOGICAL.
440
     *
441
     * @param mixed $value Value to check
442
     *
443
     * @return bool
444
     */
445 16
    public static function isLogical($value = null)
446
    {
447 16
        $value = self::flattenSingleValue($value);
448
449 16
        return is_bool($value);
450
    }
451
452
    /**
453
     * IS_TEXT.
454
     *
455
     * @param mixed $value Value to check
456
     *
457
     * @return bool
458
     */
459 32
    public static function isText($value = null)
460
    {
461 32
        $value = self::flattenSingleValue($value);
462
463 32
        return is_string($value) && !self::isError($value);
464
    }
465
466
    /**
467
     * IS_NONTEXT.
468
     *
469
     * @param mixed $value Value to check
470
     *
471
     * @return bool
472
     */
473 16
    public static function isNonText($value = null)
474
    {
475 16
        return !self::isText($value);
476
    }
477
478
    /**
479
     * N.
480
     *
481
     * Returns a value converted to a number
482
     *
483
     * @param null|mixed $value The value you want converted
484
     *
485
     * @return number N converts values listed in the following table
486
     *        If value is or refers to N returns
487
     *        A number            That number
488
     *        A date                The serial number of that date
489
     *        TRUE                1
490
     *        FALSE                0
491
     *        An error value        The error value
492
     *        Anything else        0
493
     */
494 20
    public static function n($value = null)
495
    {
496 20
        while (is_array($value)) {
497 9
            $value = array_shift($value);
498
        }
499
500 20
        switch (gettype($value)) {
501 20
            case 'double':
502 19
            case 'float':
503 19
            case 'integer':
504 8
                return $value;
505 12
            case 'boolean':
506 1
                return (int) $value;
507 11
            case 'string':
508
                //    Errors
509 8
                if ((strlen($value) > 0) && ($value[0] == '#')) {
510 2
                    return $value;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $value returns the type string which is incompatible with the documented return type double|integer.
Loading history...
511
                }
512
513 6
                break;
514
        }
515
516 9
        return 0;
517
    }
518
519
    /**
520
     * TYPE.
521
     *
522
     * Returns a number that identifies the type of a value
523
     *
524
     * @param null|mixed $value The value you want tested
525
     *
526
     * @return number N converts values listed in the following table
527
     *        If value is or refers to N returns
528
     *        A number            1
529
     *        Text                2
530
     *        Logical Value        4
531
     *        An error value        16
532
     *        Array or Matrix        64
533
     */
534 16
    public static function TYPE($value = null)
535
    {
536 16
        $value = self::flattenArrayIndexed($value);
537 16
        if (is_array($value) && (count($value) > 1)) {
538 3
            end($value);
539 3
            $a = key($value);
540
            //    Range of cells is an error
541 3
            if (self::isCellValue($a)) {
542
                return 16;
543
            //    Test for Matrix
544 3
            } elseif (self::isMatrixValue($a)) {
545 3
                return 64;
546
            }
547
        } elseif (empty($value)) {
548
            //    Empty Cell
549 2
            return 1;
550
        }
551 11
        $value = self::flattenSingleValue($value);
552
553 11
        if (($value === null) || (is_float($value)) || (is_int($value))) {
554 4
            return 1;
555 7
        } elseif (is_bool($value)) {
556 1
            return 4;
557 6
        } elseif (is_array($value)) {
558
            return 64;
559 6
        } elseif (is_string($value)) {
560
            //    Errors
561 6
            if ((strlen($value) > 0) && ($value[0] == '#')) {
562 2
                return 16;
563
            }
564
565 4
            return 2;
566
        }
567
568
        return 0;
569
    }
570
571
    /**
572
     * Convert a multi-dimensional array to a simple 1-dimensional array.
573
     *
574
     * @param array $array Array to be flattened
575
     *
576
     * @return array Flattened array
577
     */
578 395
    public static function flattenArray($array)
579
    {
580 395
        if (!is_array($array)) {
0 ignored issues
show
introduced by
The condition is_array($array) is always true.
Loading history...
581 6
            return (array) $array;
582
        }
583
584 389
        $arrayValues = [];
585 389
        foreach ($array as $value) {
586 363
            if (is_array($value)) {
587 107
                foreach ($value as $val) {
588 107
                    if (is_array($val)) {
589 36
                        foreach ($val as $v) {
590 36
                            $arrayValues[] = $v;
591
                        }
592
                    } else {
593 72
                        $arrayValues[] = $val;
594
                    }
595
                }
596
            } else {
597 278
                $arrayValues[] = $value;
598
            }
599
        }
600
601 389
        return $arrayValues;
602
    }
603
604
    /**
605
     * Convert a multi-dimensional array to a simple 1-dimensional array, but retain an element of indexing.
606
     *
607
     * @param array $array Array to be flattened
608
     *
609
     * @return array Flattened array
610
     */
611 79
    public static function flattenArrayIndexed($array)
612
    {
613 79
        if (!is_array($array)) {
0 ignored issues
show
introduced by
The condition is_array($array) is always true.
Loading history...
614 11
            return (array) $array;
615
        }
616
617 68
        $arrayValues = [];
618 68
        foreach ($array as $k1 => $value) {
619 68
            if (is_array($value)) {
620 65
                foreach ($value as $k2 => $val) {
621 65
                    if (is_array($val)) {
622 26
                        foreach ($val as $k3 => $v) {
623 26
                            $arrayValues[$k1 . '.' . $k2 . '.' . $k3] = $v;
624
                        }
625
                    } else {
626 50
                        $arrayValues[$k1 . '.' . $k2] = $val;
627
                    }
628
                }
629
            } else {
630 26
                $arrayValues[$k1] = $value;
631
            }
632
        }
633
634 68
        return $arrayValues;
635
    }
636
637
    /**
638
     * Convert an array to a single scalar value by extracting the first element.
639
     *
640
     * @param mixed $value Array or scalar value
641
     *
642
     * @return mixed
643
     */
644 3763
    public static function flattenSingleValue($value = '')
645
    {
646 3763
        while (is_array($value)) {
647 69
            $value = array_pop($value);
648
        }
649
650 3763
        return $value;
651
    }
652
653
    /**
654
     * ISFORMULA.
655
     *
656
     * @param mixed $cellReference The cell to check
657
     * @param Cell $pCell The current cell (containing this formula)
658
     *
659
     * @return bool|string
660
     */
661 19
    public static function isFormula($cellReference = '', Cell $pCell = null)
662
    {
663 19
        if ($pCell === null) {
664
            return self::REF();
665
        }
666
667 19
        preg_match('/^' . Calculation::CALCULATION_REGEXP_CELLREF . '$/i', $cellReference, $matches);
668
669 19
        $cellReference = $matches[6] . $matches[7];
670 19
        $worksheetName = trim($matches[3], "'");
671
672 19
        $worksheet = (!empty($worksheetName))
673 3
            ? $pCell->getWorksheet()->getParent()->getSheetByName($worksheetName)
674 19
            : $pCell->getWorksheet();
675
676 19
        return $worksheet->getCell($cellReference)->isFormula();
1 ignored issue
show
Bug introduced by
The method getCell() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

676
        return $worksheet->/** @scrutinizer ignore-call */ getCell($cellReference)->isFormula();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
677
    }
678
}
679