Completed
Push — develop ( 91417a...f02c33 )
by Adrien
18:11
created

MathTrig::ATAN2()   C

Complexity

Conditions 9
Paths 12

Size

Total Lines 22
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 9

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 9
eloc 13
c 1
b 0
f 0
nc 12
nop 2
dl 0
loc 22
ccs 13
cts 13
cp 1
crap 9
rs 6.412
1
<?php
2
3
namespace PhpSpreadsheet\Calculation;
4
5
/**
6
 * Copyright (c) 2006 - 2016 PhpSpreadsheet
7
 *
8
 * This library is free software; you can redistribute it and/or
9
 * modify it under the terms of the GNU Lesser General Public
10
 * License as published by the Free Software Foundation; either
11
 * version 2.1 of the License, or (at your option) any later version.
12
 *
13
 * This library is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16
 * Lesser General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Lesser General Public
19
 * License along with this library; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 *
22
 * @category    PhpSpreadsheet
23
 * @copyright   Copyright (c) 2006 - 2016 PhpSpreadsheet (https://github.com/PHPOffice/PhpSpreadsheet)
24
 * @license     http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt    LGPL
25
 * @version     ##VERSION##, ##DATE##
26
 */
27
class MathTrig
28
{
29
    //
30
    //    Private method to return an array of the factors of the input value
31
    //
32 35
    private static function factors($value)
33
    {
34 35
        $startVal = floor(sqrt($value));
35
36 35
        $factorArray = [];
37 35
        for ($i = $startVal; $i > 1; --$i) {
38 33
            if (($value % $i) == 0) {
39 23
                $factorArray = array_merge($factorArray, self::factors($value / $i));
40 23
                $factorArray = array_merge($factorArray, self::factors($i));
41 23
                if ($i <= sqrt($value)) {
42 23
                    break;
43
                }
44
            }
45
        }
46 35
        if (!empty($factorArray)) {
47 23
            rsort($factorArray);
48
49 23
            return $factorArray;
50
        } else {
51 35
            return [(integer) $value];
52
        }
53
    }
54
55 5
    private static function romanCut($num, $n)
56
    {
57 5
        return ($num - ($num % $n)) / $n;
58
    }
59
60
    /**
61
     * ATAN2
62
     *
63
     * This function calculates the arc tangent of the two variables x and y. It is similar to
64
     *        calculating the arc tangent of y ÷ x, except that the signs of both arguments are used
65
     *        to determine the quadrant of the result.
66
     * The arctangent is the angle from the x-axis to a line containing the origin (0, 0) and a
67
     *        point with coordinates (xCoordinate, yCoordinate). The angle is given in radians between
68
     *        -pi and pi, excluding -pi.
69
     *
70
     * Note that the Excel ATAN2() function accepts its arguments in the reverse order to the standard
71
     *        PHP atan2() function, so we need to reverse them here before calling the PHP atan() function.
72
     *
73
     * Excel Function:
74
     *        ATAN2(xCoordinate,yCoordinate)
75
     *
76
     * @category Mathematical and Trigonometric Functions
77
     * @param    float    $xCoordinate        The x-coordinate of the point.
78
     * @param    float    $yCoordinate        The y-coordinate of the point.
79
     * @return    float    The inverse tangent of the specified x- and y-coordinates.
80
     */
81 16
    public static function ATAN2($xCoordinate = null, $yCoordinate = null)
82
    {
83 16
        $xCoordinate = Functions::flattenSingleValue($xCoordinate);
84 16
        $yCoordinate = Functions::flattenSingleValue($yCoordinate);
85
86 16
        $xCoordinate = ($xCoordinate !== null) ? $xCoordinate : 0.0;
87 16
        $yCoordinate = ($yCoordinate !== null) ? $yCoordinate : 0.0;
88
89 16
        if (((is_numeric($xCoordinate)) || (is_bool($xCoordinate))) &&
90 16
            ((is_numeric($yCoordinate))) || (is_bool($yCoordinate))) {
91 15
            $xCoordinate = (float) $xCoordinate;
92 15
            $yCoordinate = (float) $yCoordinate;
93
94 15
            if (($xCoordinate == 0) && ($yCoordinate == 0)) {
95 1
                return Functions::DIV0();
96
            }
97
98 14
            return atan2($yCoordinate, $xCoordinate);
99
        }
100
101 1
        return Functions::VALUE();
102
    }
103
104
    /**
105
     * CEILING
106
     *
107
     * Returns number rounded up, away from zero, to the nearest multiple of significance.
108
     *        For example, if you want to avoid using pennies in your prices and your product is
109
     *        priced at $4.42, use the formula =CEILING(4.42,0.05) to round prices up to the
110
     *        nearest nickel.
111
     *
112
     * Excel Function:
113
     *        CEILING(number[,significance])
114
     *
115
     * @category Mathematical and Trigonometric Functions
116
     * @param    float    $number            The number you want to round.
117
     * @param    float    $significance    The multiple to which you want to round.
118
     * @return    float    Rounded Number
119
     */
120 43 View Code Duplication
    public static function CEILING($number, $significance = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
    {
122 43
        $number = Functions::flattenSingleValue($number);
123 43
        $significance = Functions::flattenSingleValue($significance);
124
125 43
        if ((is_null($significance)) &&
126 43
            (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) {
127
            $significance = $number / abs($number);
128
        }
129
130 43
        if ((is_numeric($number)) && (is_numeric($significance))) {
131 41
            if (($number == 0.0) || ($significance == 0.0)) {
132 3
                return 0.0;
133 38
            } elseif (self::SIGN($number) == self::SIGN($significance)) {
134 35
                return ceil($number / $significance) * $significance;
135
            } else {
136 3
                return Functions::NAN();
137
            }
138
        }
139
140 2
        return Functions::VALUE();
141
    }
142
143
    /**
144
     * COMBIN
145
     *
146
     * Returns the number of combinations for a given number of items. Use COMBIN to
147
     *        determine the total possible number of groups for a given number of items.
148
     *
149
     * Excel Function:
150
     *        COMBIN(numObjs,numInSet)
151
     *
152
     * @category Mathematical and Trigonometric Functions
153
     * @param    int        $numObjs    Number of different objects
154
     * @param    int        $numInSet    Number of objects in each combination
155
     * @return    int        Number of combinations
156
     */
157 24
    public static function COMBIN($numObjs, $numInSet)
158
    {
159 24
        $numObjs = Functions::flattenSingleValue($numObjs);
160 24
        $numInSet = Functions::flattenSingleValue($numInSet);
161
162 24
        if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
163 23
            if ($numObjs < $numInSet) {
164 3
                return Functions::NAN();
165 20
            } elseif ($numInSet < 0) {
166 2
                return Functions::NAN();
167
            }
168
169 18
            return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
170
        }
171
172 1
        return Functions::VALUE();
173
    }
174
175
    /**
176
     * EVEN
177
     *
178
     * Returns number rounded up to the nearest even integer.
179
     * You can use this function for processing items that come in twos. For example,
180
     *        a packing crate accepts rows of one or two items. The crate is full when
181
     *        the number of items, rounded up to the nearest two, matches the crate's
182
     *        capacity.
183
     *
184
     * Excel Function:
185
     *        EVEN(number)
186
     *
187
     * @category Mathematical and Trigonometric Functions
188
     * @param    float    $number            Number to round
189
     * @return    int        Rounded Number
190
     */
191 25
    public static function EVEN($number)
192
    {
193 25
        $number = Functions::flattenSingleValue($number);
194
195 25
        if (is_null($number)) {
196 1
            return 0;
197 24
        } elseif (is_bool($number)) {
198 2
            $number = (int) $number;
199
        }
200
201 24
        if (is_numeric($number)) {
202 23
            $significance = 2 * self::SIGN($number);
203
204 23
            return (int) self::CEILING($number, $significance);
205
        }
206
207 1
        return Functions::VALUE();
208
    }
209
210
    /**
211
     * FACT
212
     *
213
     * Returns the factorial of a number.
214
     * The factorial of a number is equal to 1*2*3*...* number.
215
     *
216
     * Excel Function:
217
     *        FACT(factVal)
218
     *
219
     * @category Mathematical and Trigonometric Functions
220
     * @param    float    $factVal    Factorial Value
221
     * @return    int        Factorial
222
     */
223 145
    public static function FACT($factVal)
224
    {
225 145
        $factVal = Functions::flattenSingleValue($factVal);
226
227 145
        if (is_numeric($factVal)) {
228 144
            if ($factVal < 0) {
229 1
                return Functions::NAN();
230
            }
231 143
            $factLoop = floor($factVal);
232 143
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
233
                if ($factVal > $factLoop) {
234
                    return Functions::NAN();
235
                }
236
            }
237
238 143
            $factorial = 1;
239 143
            while ($factLoop > 1) {
240 71
                $factorial *= $factLoop--;
241
            }
242
243 143
            return $factorial;
244
        }
245
246 1
        return Functions::VALUE();
247
    }
248
249
    /**
250
     * FACTDOUBLE
251
     *
252
     * Returns the double factorial of a number.
253
     *
254
     * Excel Function:
255
     *        FACTDOUBLE(factVal)
256
     *
257
     * @category Mathematical and Trigonometric Functions
258
     * @param    float    $factVal    Factorial Value
259
     * @return    int        Double Factorial
260
     */
261 8
    public static function FACTDOUBLE($factVal)
262
    {
263 8
        $factLoop = Functions::flattenSingleValue($factVal);
264
265 8
        if (is_numeric($factLoop)) {
266 7
            $factLoop = floor($factLoop);
267 7
            if ($factVal < 0) {
268 1
                return Functions::NAN();
269
            }
270 6
            $factorial = 1;
271 6
            while ($factLoop > 1) {
272 5
                $factorial *= $factLoop--;
273 5
                --$factLoop;
274
            }
275
276 6
            return $factorial;
277
        }
278
279 1
        return Functions::VALUE();
280
    }
281
282
    /**
283
     * FLOOR
284
     *
285
     * Rounds number down, toward zero, to the nearest multiple of significance.
286
     *
287
     * Excel Function:
288
     *        FLOOR(number[,significance])
289
     *
290
     * @category Mathematical and Trigonometric Functions
291
     * @param    float    $number            Number to round
292
     * @param    float    $significance    Significance
293
     * @return    float    Rounded Number
294
     */
295 11 View Code Duplication
    public static function FLOOR($number, $significance = null)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
296
    {
297 11
        $number = Functions::flattenSingleValue($number);
298 11
        $significance = Functions::flattenSingleValue($significance);
299
300 11
        if ((is_null($significance)) &&
301 11
            (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) {
302
            $significance = $number / abs($number);
303
        }
304
305 11
        if ((is_numeric($number)) && (is_numeric($significance))) {
306 9
            if ($significance == 0.0) {
307 1
                return Functions::DIV0();
308 8
            } elseif ($number == 0.0) {
309
                return 0.0;
310 8
            } elseif (self::SIGN($number) == self::SIGN($significance)) {
311 6
                return floor($number / $significance) * $significance;
312
            } else {
313 2
                return Functions::NAN();
314
            }
315
        }
316
317 2
        return Functions::VALUE();
318
    }
319
320
    /**
321
     * GCD
322
     *
323
     * Returns the greatest common divisor of a series of numbers.
324
     * The greatest common divisor is the largest integer that divides both
325
     *        number1 and number2 without a remainder.
326
     *
327
     * Excel Function:
328
     *        GCD(number1[,number2[, ...]])
329
     *
330
     * @category Mathematical and Trigonometric Functions
331
     * @param    mixed    $arg,...        Data values
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
332
     * @return    int                    Greatest Common Divisor
333
     */
334 24
    public static function GCD()
335
    {
336 24
        $returnValue = 1;
337 24
        $allValuesFactors = [];
338
        // Loop through arguments
339 24
        foreach (Functions::flattenArray(func_get_args()) as $value) {
340 24
            if (!is_numeric($value)) {
341 1
                return Functions::VALUE();
342 24
            } elseif ($value == 0) {
343 4
                continue;
344 23
            } elseif ($value < 0) {
345 1
                return Functions::NAN();
346
            }
347 23
            $myFactors = self::factors($value);
348 23
            $myCountedFactors = array_count_values($myFactors);
349 23
            $allValuesFactors[] = $myCountedFactors;
350
        }
351 22
        $allValuesCount = count($allValuesFactors);
352 22
        if ($allValuesCount == 0) {
353 1
            return 0;
354
        }
355
356 21
        $mergedArray = $allValuesFactors[0];
357 21
        for ($i = 1; $i < $allValuesCount; ++$i) {
358 19
            $mergedArray = array_intersect_key($mergedArray, $allValuesFactors[$i]);
359
        }
360 21
        $mergedArrayValues = count($mergedArray);
361 21
        if ($mergedArrayValues == 0) {
362 6
            return $returnValue;
363 15
        } elseif ($mergedArrayValues > 1) {
364 3
            foreach ($mergedArray as $mergedKey => $mergedValue) {
365 3
                foreach ($allValuesFactors as $highestPowerTest) {
366 3
                    foreach ($highestPowerTest as $testKey => $testValue) {
367 3
                        if (($testKey == $mergedKey) && ($testValue < $mergedValue)) {
368 2
                            $mergedArray[$mergedKey] = $testValue;
369 3
                            $mergedValue = $testValue;
370
                        }
371
                    }
372
                }
373
            }
374
375 3
            $returnValue = 1;
376 3
            foreach ($mergedArray as $key => $value) {
377 3
                $returnValue *= pow($key, $value);
378
            }
379
380 3
            return $returnValue;
381
        } else {
382 12
            $keys = array_keys($mergedArray);
383 12
            $key = $keys[0];
384 12
            $value = $mergedArray[$key];
385 12
            foreach ($allValuesFactors as $testValue) {
386 12
                foreach ($testValue as $mergedKey => $mergedValue) {
387 12
                    if (($mergedKey == $key) && ($mergedValue < $value)) {
388 12
                        $value = $mergedValue;
389
                    }
390
                }
391
            }
392
393 12
            return pow($key, $value);
394
        }
395
    }
396
397
    /**
398
     * INT
399
     *
400
     * Casts a floating point value to an integer
401
     *
402
     * Excel Function:
403
     *        INT(number)
404
     *
405
     * @category Mathematical and Trigonometric Functions
406
     * @param    float    $number            Number to cast to an integer
407
     * @return    int    Integer value
408
     */
409 19 View Code Duplication
    public static function INT($number)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
410
    {
411 19
        $number = Functions::flattenSingleValue($number);
412
413 19
        if (is_null($number)) {
414 1
            return 0;
415 18
        } elseif (is_bool($number)) {
416 2
            return (int) $number;
417
        }
418 16
        if (is_numeric($number)) {
419 15
            return (int) floor($number);
420
        }
421
422 1
        return Functions::VALUE();
423
    }
424
425
    /**
426
     * LCM
427
     *
428
     * Returns the lowest common multiplier of a series of numbers
429
     * The least common multiple is the smallest positive integer that is a multiple
430
     * of all integer arguments number1, number2, and so on. Use LCM to add fractions
431
     * with different denominators.
432
     *
433
     * Excel Function:
434
     *        LCM(number1[,number2[, ...]])
435
     *
436
     * @category Mathematical and Trigonometric Functions
437
     * @param    mixed    $arg,...        Data values
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
438
     * @return    int        Lowest Common Multiplier
439
     */
440 12
    public static function LCM()
441
    {
442 12
        $returnValue = 1;
443 12
        $allPoweredFactors = [];
444
        // Loop through arguments
445 12
        foreach (Functions::flattenArray(func_get_args()) as $value) {
446 12
            if (!is_numeric($value)) {
447 1
                return Functions::VALUE();
448
            }
449 12
            if ($value == 0) {
450 1
                return 0;
451 12
            } elseif ($value < 0) {
452 1
                return Functions::NAN();
453
            }
454 12
            $myFactors = self::factors(floor($value));
455 12
            $myCountedFactors = array_count_values($myFactors);
456 12
            $myPoweredFactors = [];
457 12
            foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
458 12
                $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor, $myCountedPower);
459
            }
460 12
            foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
461 12
                if (array_key_exists($myPoweredValue, $allPoweredFactors)) {
462 6
                    if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
463 6
                        $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
464
                    }
465
                } else {
466 12
                    $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
467
                }
468
            }
469
        }
470 9
        foreach ($allPoweredFactors as $allPoweredFactor) {
471 9
            $returnValue *= (integer) $allPoweredFactor;
472
        }
473
474 9
        return $returnValue;
475
    }
476
477
    /**
478
     * LOG_BASE
479
     *
480
     * Returns the logarithm of a number to a specified base. The default base is 10.
481
     *
482
     * Excel Function:
483
     *        LOG(number[,base])
484
     *
485
     * @category Mathematical and Trigonometric Functions
486
     * @param    float    $number        The positive real number for which you want the logarithm
487
     * @param    float    $base        The base of the logarithm. If base is omitted, it is assumed to be 10.
488
     * @return    float
489
     */
490 69
    public static function logBase($number = null, $base = 10)
491
    {
492 69
        $number = Functions::flattenSingleValue($number);
493 69
        $base = (is_null($base)) ? 10 : (float) Functions::flattenSingleValue($base);
494
495 69
        if ((!is_numeric($base)) || (!is_numeric($number))) {
496 2
            return Functions::VALUE();
497
        }
498 67
        if (($base <= 0) || ($number <= 0)) {
499 18
            return Functions::NAN();
500
        }
501
502 49
        return log($number, $base);
503
    }
504
505
    /**
506
     * MDETERM
507
     *
508
     * Returns the matrix determinant of an array.
509
     *
510
     * Excel Function:
511
     *        MDETERM(array)
512
     *
513
     * @category Mathematical and Trigonometric Functions
514
     * @param    array    $matrixValues    A matrix of values
515
     * @return    float
516
     */
517 14 View Code Duplication
    public static function MDETERM($matrixValues)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
518
    {
519 14
        $matrixData = [];
520 14
        if (!is_array($matrixValues)) {
521
            $matrixValues = [[$matrixValues]];
522
        }
523
524 14
        $row = $maxColumn = 0;
525 14
        foreach ($matrixValues as $matrixRow) {
526 14
            if (!is_array($matrixRow)) {
527
                $matrixRow = [$matrixRow];
528
            }
529 14
            $column = 0;
530 14
            foreach ($matrixRow as $matrixCell) {
531 14
                if ((is_string($matrixCell)) || ($matrixCell === null)) {
532 1
                    return Functions::VALUE();
533
                }
534 14
                $matrixData[$column][$row] = $matrixCell;
535 14
                ++$column;
536
            }
537 14
            if ($column > $maxColumn) {
538 14
                $maxColumn = $column;
539
            }
540 14
            ++$row;
541
        }
542 13
        if ($row != $maxColumn) {
543 1
            return Functions::VALUE();
544
        }
545
546
        try {
547 12
            $matrix = new \PhpSpreadsheet\Shared\JAMA\Matrix($matrixData);
548
549 12
            return $matrix->det();
550
        } catch (\PhpSpreadsheet\Exception $ex) {
551
            return Functions::VALUE();
552
        }
553
    }
554
555
    /**
556
     * MINVERSE
557
     *
558
     * Returns the inverse matrix for the matrix stored in an array.
559
     *
560
     * Excel Function:
561
     *        MINVERSE(array)
562
     *
563
     * @category Mathematical and Trigonometric Functions
564
     * @param    array    $matrixValues    A matrix of values
565
     * @return    array
566
     */
567 View Code Duplication
    public static function MINVERSE($matrixValues)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
568
    {
569
        $matrixData = [];
570
        if (!is_array($matrixValues)) {
571
            $matrixValues = [[$matrixValues]];
572
        }
573
574
        $row = $maxColumn = 0;
575
        foreach ($matrixValues as $matrixRow) {
576
            if (!is_array($matrixRow)) {
577
                $matrixRow = [$matrixRow];
578
            }
579
            $column = 0;
580
            foreach ($matrixRow as $matrixCell) {
581
                if ((is_string($matrixCell)) || ($matrixCell === null)) {
582
                    return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \PhpSpreadsheet\C...ion\Functions::VALUE(); (string) is incompatible with the return type documented by PhpSpreadsheet\Calculation\MathTrig::MINVERSE of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
583
                }
584
                $matrixData[$column][$row] = $matrixCell;
585
                ++$column;
586
            }
587
            if ($column > $maxColumn) {
588
                $maxColumn = $column;
589
            }
590
            ++$row;
591
        }
592
        foreach ($matrixValues as $matrixRow) {
593
            if (count($matrixRow) != $maxColumn) {
594
                return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \PhpSpreadsheet\C...ion\Functions::VALUE(); (string) is incompatible with the return type documented by PhpSpreadsheet\Calculation\MathTrig::MINVERSE of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
595
            }
596
        }
597
598
        try {
599
            $matrix = new \PhpSpreadsheet\Shared\JAMA\Matrix($matrixData);
600
601
            return $matrix->inverse()->getArray();
602
        } catch (\PhpSpreadsheet\Exception $ex) {
603
            return Functions::VALUE();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return \PhpSpreadsheet\C...ion\Functions::VALUE(); (string) is incompatible with the return type documented by PhpSpreadsheet\Calculation\MathTrig::MINVERSE of type array.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
604
        }
605
    }
606
607
    /**
608
     * MMULT
609
     *
610
     * @param    array    $matrixData1    A matrix of values
611
     * @param    array    $matrixData2    A matrix of values
612
     * @return    array
613
     */
614
    public static function MMULT($matrixData1, $matrixData2)
615
    {
616
        $matrixAData = $matrixBData = [];
617
        if (!is_array($matrixData1)) {
618
            $matrixData1 = [[$matrixData1]];
619
        }
620
        if (!is_array($matrixData2)) {
621
            $matrixData2 = [[$matrixData2]];
622
        }
623
624
        try {
625
            $rowA = 0;
626 View Code Duplication
            foreach ($matrixData1 as $matrixRow) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
627
                if (!is_array($matrixRow)) {
628
                    $matrixRow = [$matrixRow];
629
                }
630
                $columnA = 0;
631
                foreach ($matrixRow as $matrixCell) {
632
                    if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
633
                        return Functions::VALUE();
634
                    }
635
                    $matrixAData[$rowA][$columnA] = $matrixCell;
636
                    ++$columnA;
637
                }
638
                ++$rowA;
639
            }
640
            $matrixA = new \PhpSpreadsheet\Shared\JAMA\Matrix($matrixAData);
641
            $rowB = 0;
642 View Code Duplication
            foreach ($matrixData2 as $matrixRow) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
643
                if (!is_array($matrixRow)) {
644
                    $matrixRow = [$matrixRow];
645
                }
646
                $columnB = 0;
647
                foreach ($matrixRow as $matrixCell) {
648
                    if ((!is_numeric($matrixCell)) || ($matrixCell === null)) {
649
                        return Functions::VALUE();
650
                    }
651
                    $matrixBData[$rowB][$columnB] = $matrixCell;
652
                    ++$columnB;
653
                }
654
                ++$rowB;
655
            }
656
            $matrixB = new \PhpSpreadsheet\Shared\JAMA\Matrix($matrixBData);
657
658
            if ($columnA != $rowB) {
0 ignored issues
show
Bug introduced by
The variable $columnA does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
659
                return Functions::VALUE();
660
            }
661
662
            return $matrixA->times($matrixB)->getArray();
663
        } catch (\PhpSpreadsheet\Exception $ex) {
664
            return Functions::VALUE();
665
        }
666
    }
667
668
    /**
669
     * MOD
670
     *
671
     * @param    int        $a        Dividend
672
     * @param    int        $b        Divisor
673
     * @return    int        Remainder
674
     */
675 9
    public static function MOD($a = 1, $b = 1)
676
    {
677 9
        $a = Functions::flattenSingleValue($a);
678 9
        $b = Functions::flattenSingleValue($b);
679
680 9
        if ($b == 0.0) {
681 1
            return Functions::DIV0();
682 8
        } elseif (($a < 0.0) && ($b > 0.0)) {
683 1
            return $b - fmod(abs($a), $b);
684 7
        } elseif (($a > 0.0) && ($b < 0.0)) {
685 2
            return $b + fmod($a, abs($b));
686
        }
687
688 5
        return fmod($a, $b);
689
    }
690
691
    /**
692
     * MROUND
693
     *
694
     * Rounds a number to the nearest multiple of a specified value
695
     *
696
     * @param    float    $number            Number to round
697
     * @param    int        $multiple        Multiple to which you want to round $number
698
     * @return    float    Rounded Number
699
     */
700 13
    public static function MROUND($number, $multiple)
701
    {
702 13
        $number = Functions::flattenSingleValue($number);
703 13
        $multiple = Functions::flattenSingleValue($multiple);
704
705 13
        if ((is_numeric($number)) && (is_numeric($multiple))) {
706 11
            if ($multiple == 0) {
707 1
                return 0;
708
            }
709 10
            if ((self::SIGN($number)) == (self::SIGN($multiple))) {
710 9
                $multiplier = 1 / $multiple;
711
712 9
                return round($number * $multiplier) / $multiplier;
713
            }
714
715 1
            return Functions::NAN();
716
        }
717
718 2
        return Functions::VALUE();
719
    }
720
721
    /**
722
     * MULTINOMIAL
723
     *
724
     * Returns the ratio of the factorial of a sum of values to the product of factorials.
725
     *
726
     * @param    array of mixed        Data Series
727
     * @return    float
728
     */
729 2
    public static function MULTINOMIAL()
730
    {
731 2
        $summer = 0;
732 2
        $divisor = 1;
733
        // Loop through arguments
734 2
        foreach (Functions::flattenArray(func_get_args()) as $arg) {
735
            // Is it a numeric value?
736 2
            if (is_numeric($arg)) {
737 2
                if ($arg < 1) {
738
                    return Functions::NAN();
739
                }
740 2
                $summer += floor($arg);
741 2
                $divisor *= self::FACT($arg);
742
            } else {
743 2
                return Functions::VALUE();
744
            }
745
        }
746
747
        // Return
748 2
        if ($summer > 0) {
749 2
            $summer = self::FACT($summer);
750
751 2
            return $summer / $divisor;
752
        }
753
754
        return 0;
755
    }
756
757
    /**
758
     * ODD
759
     *
760
     * Returns number rounded up to the nearest odd integer.
761
     *
762
     * @param    float    $number            Number to round
763
     * @return    int        Rounded Number
764
     */
765 13
    public static function ODD($number)
766
    {
767 13
        $number = Functions::flattenSingleValue($number);
768
769 13
        if (is_null($number)) {
770 1
            return 1;
771 12
        } elseif (is_bool($number)) {
772 2
            return 1;
773 10
        } elseif (is_numeric($number)) {
774 9
            $significance = self::SIGN($number);
775 9
            if ($significance == 0) {
776 1
                return 1;
777
            }
778
779 8
            $result = self::CEILING($number, $significance);
780 8
            if ($result == self::EVEN($result)) {
781 5
                $result += $significance;
782
            }
783
784 8
            return (int) $result;
785
        }
786
787 1
        return Functions::VALUE();
788
    }
789
790
    /**
791
     * POWER
792
     *
793
     * Computes x raised to the power y.
794
     *
795
     * @param    float        $x
796
     * @param    float        $y
797
     * @return    float
798
     */
799 81
    public static function POWER($x = 0, $y = 2)
800
    {
801 81
        $x = Functions::flattenSingleValue($x);
802 81
        $y = Functions::flattenSingleValue($y);
803
804
        // Validate parameters
805 81
        if ($x == 0.0 && $y == 0.0) {
806 1
            return Functions::NAN();
807 80
        } elseif ($x == 0.0 && $y < 0.0) {
808 2
            return Functions::DIV0();
809
        }
810
811
        // Return
812 78
        $result = pow($x, $y);
813
814 78
        return (!is_nan($result) && !is_infinite($result)) ? $result : Functions::NAN();
815
    }
816
817
    /**
818
     * PRODUCT
819
     *
820
     * PRODUCT returns the product of all the values and cells referenced in the argument list.
821
     *
822
     * Excel Function:
823
     *        PRODUCT(value1[,value2[, ...]])
824
     *
825
     * @category Mathematical and Trigonometric Functions
826
     * @param    mixed        $arg,...        Data values
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
827
     * @return    float
828
     */
829 7 View Code Duplication
    public static function PRODUCT()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
830
    {
831
        // Return value
832 7
        $returnValue = null;
833
834
        // Loop through arguments
835 7
        foreach (Functions::flattenArray(func_get_args()) as $arg) {
836
            // Is it a numeric value?
837 7
            if ((is_numeric($arg)) && (!is_string($arg))) {
838 7
                if (is_null($returnValue)) {
839 7
                    $returnValue = $arg;
840
                } else {
841 7
                    $returnValue *= $arg;
842
                }
843
            }
844
        }
845
846
        // Return
847 7
        if (is_null($returnValue)) {
848
            return 0;
849
        }
850
851 7
        return $returnValue;
852
    }
853
854
    /**
855
     * QUOTIENT
856
     *
857
     * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
858
     *        and denominator is the divisor.
859
     *
860
     * Excel Function:
861
     *        QUOTIENT(value1[,value2[, ...]])
862
     *
863
     * @category Mathematical and Trigonometric Functions
864
     * @param    mixed        $arg,...        Data values
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
865
     * @return    float
866
     */
867 6
    public static function QUOTIENT()
868
    {
869
        // Return value
870 6
        $returnValue = null;
871
872
        // Loop through arguments
873 6
        foreach (Functions::flattenArray(func_get_args()) as $arg) {
874
            // Is it a numeric value?
875 6
            if ((is_numeric($arg)) && (!is_string($arg))) {
876 6
                if (is_null($returnValue)) {
877 6
                    $returnValue = ($arg == 0) ? 0 : $arg;
878
                } else {
879 6
                    if (($returnValue == 0) || ($arg == 0)) {
880
                        $returnValue = 0;
881
                    } else {
882 6
                        $returnValue /= $arg;
883
                    }
884
                }
885
            }
886
        }
887
888
        // Return
889 6
        return intval($returnValue);
890
    }
891
892
    /**
893
     * RAND
894
     *
895
     * @param    int        $min    Minimal value
896
     * @param    int        $max    Maximal value
897
     * @return    int        Random number
898
     */
899
    public static function RAND($min = 0, $max = 0)
900
    {
901
        $min = Functions::flattenSingleValue($min);
902
        $max = Functions::flattenSingleValue($max);
903
904
        if ($min == 0 && $max == 0) {
905
            return (mt_rand(0, 10000000)) / 10000000;
906
        } else {
907
            return mt_rand($min, $max);
908
        }
909
    }
910
911 5
    public static function ROMAN($aValue, $style = 0)
912
    {
913 5
        $aValue = Functions::flattenSingleValue($aValue);
914 5
        $style = (is_null($style)) ? 0 : (integer) Functions::flattenSingleValue($style);
0 ignored issues
show
Unused Code introduced by
$style is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
915 5 View Code Duplication
        if ((!is_numeric($aValue)) || ($aValue < 0) || ($aValue >= 4000)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
916
            return Functions::VALUE();
917
        }
918 5
        $aValue = (integer) $aValue;
919 5
        if ($aValue == 0) {
920
            return '';
921
        }
922
923 5
        $mill = ['', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM'];
924 5
        $cent = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
925 5
        $tens = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
926 5
        $ones = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
927
928 5
        $roman = '';
929 5
        while ($aValue > 5999) {
930
            $roman .= 'M';
931
            $aValue -= 1000;
932
        }
933 5
        $m = self::romanCut($aValue, 1000);
934 5
        $aValue %= 1000;
935 5
        $c = self::romanCut($aValue, 100);
936 5
        $aValue %= 100;
937 5
        $t = self::romanCut($aValue, 10);
938 5
        $aValue %= 10;
939
940 5
        return $roman . $mill[$m] . $cent[$c] . $tens[$t] . $ones[$aValue];
941
    }
942
943
    /**
944
     * ROUNDUP
945
     *
946
     * Rounds a number up to a specified number of decimal places
947
     *
948
     * @param    float    $number            Number to round
949
     * @param    int        $digits            Number of digits to which you want to round $number
950
     * @return    float    Rounded Number
951
     */
952 14 View Code Duplication
    public static function ROUNDUP($number, $digits)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
953
    {
954 14
        $number = Functions::flattenSingleValue($number);
955 14
        $digits = Functions::flattenSingleValue($digits);
956
957 14
        if ((is_numeric($number)) && (is_numeric($digits))) {
958 12
            $significance = pow(10, (int) $digits);
959 12
            if ($number < 0.0) {
960 2
                return floor($number * $significance) / $significance;
961
            } else {
962 10
                return ceil($number * $significance) / $significance;
963
            }
964
        }
965
966 2
        return Functions::VALUE();
967
    }
968
969
    /**
970
     * ROUNDDOWN
971
     *
972
     * Rounds a number down to a specified number of decimal places
973
     *
974
     * @param    float    $number            Number to round
975
     * @param    int        $digits            Number of digits to which you want to round $number
976
     * @return    float    Rounded Number
977
     */
978 14 View Code Duplication
    public static function ROUNDDOWN($number, $digits)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
979
    {
980 14
        $number = Functions::flattenSingleValue($number);
981 14
        $digits = Functions::flattenSingleValue($digits);
982
983 14
        if ((is_numeric($number)) && (is_numeric($digits))) {
984 12
            $significance = pow(10, (int) $digits);
985 12
            if ($number < 0.0) {
986 2
                return ceil($number * $significance) / $significance;
987
            } else {
988 10
                return floor($number * $significance) / $significance;
989
            }
990
        }
991
992 2
        return Functions::VALUE();
993
    }
994
995
    /**
996
     * SERIESSUM
997
     *
998
     * Returns the sum of a power series
999
     *
1000
     * @param    float            $x    Input value to the power series
0 ignored issues
show
Bug introduced by
There is no parameter named $x. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1001
     * @param    float            $n    Initial power to which you want to raise $x
0 ignored issues
show
Bug introduced by
There is no parameter named $n. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1002
     * @param    float            $m    Step by which to increase $n for each term in the series
0 ignored issues
show
Bug introduced by
There is no parameter named $m. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1003
     * @param    array of mixed        Data Series
1004
     * @return    float
1005
     */
1006 2
    public static function SERIESSUM()
1007
    {
1008 2
        $returnValue = 0;
1009
1010
        // Loop through arguments
1011 2
        $aArgs = Functions::flattenArray(func_get_args());
1012
1013 2
        $x = array_shift($aArgs);
1014 2
        $n = array_shift($aArgs);
1015 2
        $m = array_shift($aArgs);
1016
1017 2
        if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
1018
            // Calculate
1019 2
            $i = 0;
1020 2
            foreach ($aArgs as $arg) {
1021
                // Is it a numeric value?
1022 2
                if ((is_numeric($arg)) && (!is_string($arg))) {
1023 2
                    $returnValue += $arg * pow($x, $n + ($m * $i++));
1024
                } else {
1025 2
                    return Functions::VALUE();
1026
                }
1027
            }
1028
1029 2
            return $returnValue;
1030
        }
1031
1032
        return Functions::VALUE();
1033
    }
1034
1035
    /**
1036
     * SIGN
1037
     *
1038
     * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
1039
     *        if the number is 0, and -1 if the number is negative.
1040
     *
1041
     * @param    float    $number            Number to round
1042
     * @return    int        sign value
1043
     */
1044 72 View Code Duplication
    public static function SIGN($number)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1045
    {
1046 72
        $number = Functions::flattenSingleValue($number);
1047
1048 72
        if (is_bool($number)) {
1049 2
            return (int) $number;
1050
        }
1051 70
        if (is_numeric($number)) {
1052 69
            if ($number == 0.0) {
1053 4
                return 0;
1054
            }
1055
1056 65
            return $number / abs($number);
1057
        }
1058
1059 1
        return Functions::VALUE();
1060
    }
1061
1062
    /**
1063
     * SQRTPI
1064
     *
1065
     * Returns the square root of (number * pi).
1066
     *
1067
     * @param    float    $number        Number
1068
     * @return    float    Square Root of Number * Pi
1069
     */
1070 15 View Code Duplication
    public static function SQRTPI($number)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1071
    {
1072 15
        $number = Functions::flattenSingleValue($number);
1073
1074 15
        if (is_numeric($number)) {
1075 14
            if ($number < 0) {
1076 3
                return Functions::NAN();
1077
            }
1078
1079 11
            return sqrt($number * M_PI);
1080
        }
1081
1082 1
        return Functions::VALUE();
1083
    }
1084
1085
    /**
1086
     * SUBTOTAL
1087
     *
1088
     * Returns a subtotal in a list or database.
1089
     *
1090
     * @param    int        the number 1 to 11 that specifies which function to
1091
     *                    use in calculating subtotals within a list.
1092
     * @param    array of mixed        Data Series
1093
     * @return    float
1094
     */
1095
    public static function SUBTOTAL()
1096
    {
1097
        $aArgs = Functions::flattenArray(func_get_args());
1098
1099
        // Calculate
1100
        $subtotal = array_shift($aArgs);
1101
1102
        if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
1103
            switch ($subtotal) {
1104
                case 1:
1105
                    return Statistical::AVERAGE($aArgs);
1106
                case 2:
1107
                    return Statistical::COUNT($aArgs);
1108
                case 3:
1109
                    return Statistical::COUNTA($aArgs);
1110
                case 4:
1111
                    return Statistical::MAX($aArgs);
1112
                case 5:
1113
                    return Statistical::MIN($aArgs);
1114
                case 6:
1115
                    return self::PRODUCT($aArgs);
1116
                case 7:
1117
                    return Statistical::STDEV($aArgs);
1118
                case 8:
1119
                    return Statistical::STDEVP($aArgs);
1120
                case 9:
1121
                    return self::SUM($aArgs);
1122
                case 10:
1123
                    return Statistical::VARFunc($aArgs);
1124
                case 11:
1125
                    return Statistical::VARP($aArgs);
1126
            }
1127
        }
1128
1129
        return Functions::VALUE();
1130
    }
1131
1132
    /**
1133
     * SUM
1134
     *
1135
     * SUM computes the sum of all the values and cells referenced in the argument list.
1136
     *
1137
     * Excel Function:
1138
     *        SUM(value1[,value2[, ...]])
1139
     *
1140
     * @category Mathematical and Trigonometric Functions
1141
     * @param    mixed        $arg,...        Data values
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1142
     * @return    float
1143
     */
1144 View Code Duplication
    public static function SUM()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1145
    {
1146
        $returnValue = 0;
1147
1148
        // Loop through the arguments
1149
        foreach (Functions::flattenArray(func_get_args()) as $arg) {
1150
            // Is it a numeric value?
1151
            if ((is_numeric($arg)) && (!is_string($arg))) {
1152
                $returnValue += $arg;
1153
            }
1154
        }
1155
1156
        return $returnValue;
1157
    }
1158
1159
    /**
1160
     * SUMIF
1161
     *
1162
     * Counts the number of cells that contain numbers within the list of arguments
1163
     *
1164
     * Excel Function:
1165
     *        SUMIF(value1[,value2[, ...]],condition)
1166
     *
1167
     * @category Mathematical and Trigonometric Functions
1168
     * @param    mixed        $arg,...        Data values
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1169
     * @param    string        $condition        The criteria that defines which cells will be summed.
1170
     * @return    float
1171
     */
1172 5
    public static function SUMIF($aArgs, $condition, $sumArgs = [])
1173
    {
1174 5
        $returnValue = 0;
1175
1176 5
        $aArgs = Functions::flattenArray($aArgs);
1177 5
        $sumArgs = Functions::flattenArray($sumArgs);
1178 5
        if (empty($sumArgs)) {
1179 1
            $sumArgs = $aArgs;
1180
        }
1181 5
        $condition = Functions::ifCondition($condition);
1182
        // Loop through arguments
1183 5
        foreach ($aArgs as $key => $arg) {
1184 5
            if (!is_numeric($arg)) {
1185 4
                $arg = str_replace('"', '""', $arg);
1186 4
                $arg = \PhpSpreadsheet\Calculation::wrapResult(strtoupper($arg));
1187
            }
1188
1189 5
            $testCondition = '=' . $arg . $condition;
1190 5
            if (\PhpSpreadsheet\Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
1191
                // Is it a value within our criteria
1192 5
                $returnValue += $sumArgs[$key];
1193
            }
1194
        }
1195
1196 5
        return $returnValue;
1197
    }
1198
1199
    /**
1200
     *    SUMIFS
1201
     *
1202
     *    Counts the number of cells that contain numbers within the list of arguments
1203
     *
1204
     *    Excel Function:
1205
     *        SUMIFS(value1[,value2[, ...]],condition)
1206
     *
1207
     *    @category Mathematical and Trigonometric Functions
1208
     *    @param    mixed        $arg,...        Data values
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1209
     *    @param    string        $condition        The criteria that defines which cells will be summed.
0 ignored issues
show
Bug introduced by
There is no parameter named $condition. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1210
     *    @return    float
1211
     */
1212
    public static function SUMIFS()
1213
    {
1214
        $arrayList = func_get_args();
1215
1216
        // Return value
1217
        $returnValue = 0;
1218
1219
        $sumArgs = Functions::flattenArray(array_shift($arrayList));
1220
1221
        while (count($arrayList) > 0) {
1222
            $aArgsArray[] = Functions::flattenArray(array_shift($arrayList));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$aArgsArray was never initialized. Although not strictly required by PHP, it is generally a good practice to add $aArgsArray = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1223
            $conditions[] = Functions::ifCondition(array_shift($arrayList));
0 ignored issues
show
Coding Style Comprehensibility introduced by
$conditions was never initialized. Although not strictly required by PHP, it is generally a good practice to add $conditions = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1224
        }
1225
1226
        // Loop through each set of arguments and conditions
1227
        foreach ($conditions as $index => $condition) {
0 ignored issues
show
Bug introduced by
The variable $conditions does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1228
            $aArgs = $aArgsArray[$index];
0 ignored issues
show
Bug introduced by
The variable $aArgsArray does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
1229
1230
            // Loop through arguments
1231
            foreach ($aArgs as $key => $arg) {
1232
                if (!is_numeric($arg)) {
1233
                    $arg = \PhpSpreadsheet\Calculation::wrapResult(strtoupper($arg));
1234
                }
1235
                $testCondition = '=' . $arg . $condition;
1236
                if (\PhpSpreadsheet\Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
1237
                    // Is it a value within our criteria
1238
                    $returnValue += $sumArgs[$key];
1239
                }
1240
            }
1241
        }
1242
1243
        // Return
1244
        return $returnValue;
1245
    }
1246
1247
    /**
1248
     * SUMPRODUCT
1249
     *
1250
     * Excel Function:
1251
     *        SUMPRODUCT(value1[,value2[, ...]])
1252
     *
1253
     * @category Mathematical and Trigonometric Functions
1254
     * @param    mixed        $arg,...        Data values
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1255
     * @return    float
1256
     */
1257
    public static function SUMPRODUCT()
1258
    {
1259
        $arrayList = func_get_args();
1260
1261
        $wrkArray = Functions::flattenArray(array_shift($arrayList));
1262
        $wrkCellCount = count($wrkArray);
1263
1264
        for ($i = 0; $i < $wrkCellCount; ++$i) {
1265
            if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
1266
                $wrkArray[$i] = 0;
1267
            }
1268
        }
1269
1270
        foreach ($arrayList as $matrixData) {
1271
            $array2 = Functions::flattenArray($matrixData);
1272
            $count = count($array2);
1273
            if ($wrkCellCount != $count) {
1274
                return Functions::VALUE();
1275
            }
1276
1277
            foreach ($array2 as $i => $val) {
1278
                if ((!is_numeric($val)) || (is_string($val))) {
1279
                    $val = 0;
1280
                }
1281
                $wrkArray[$i] *= $val;
1282
            }
1283
        }
1284
1285
        return array_sum($wrkArray);
1286
    }
1287
1288
    /**
1289
     * SUMSQ
1290
     *
1291
     * SUMSQ returns the sum of the squares of the arguments
1292
     *
1293
     * Excel Function:
1294
     *        SUMSQ(value1[,value2[, ...]])
1295
     *
1296
     * @category Mathematical and Trigonometric Functions
1297
     * @param    mixed        $arg,...        Data values
0 ignored issues
show
Bug introduced by
There is no parameter named $arg,.... Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
1298
     * @return    float
1299
     */
1300 7 View Code Duplication
    public static function SUMSQ()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1301
    {
1302 7
        $returnValue = 0;
1303
1304
        // Loop through arguments
1305 7
        foreach (Functions::flattenArray(func_get_args()) as $arg) {
1306
            // Is it a numeric value?
1307 7
            if ((is_numeric($arg)) && (!is_string($arg))) {
1308 7
                $returnValue += ($arg * $arg);
1309
            }
1310
        }
1311
1312 7
        return $returnValue;
1313
    }
1314
1315
    /**
1316
     * SUMX2MY2
1317
     *
1318
     * @param    mixed[]    $matrixData1    Matrix #1
1319
     * @param    mixed[]    $matrixData2    Matrix #2
1320
     * @return    float
1321
     */
1322 View Code Duplication
    public static function SUMX2MY2($matrixData1, $matrixData2)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1323
    {
1324
        $array1 = Functions::flattenArray($matrixData1);
1325
        $array2 = Functions::flattenArray($matrixData2);
1326
        $count = min(count($array1), count($array2));
1327
1328
        $result = 0;
1329
        for ($i = 0; $i < $count; ++$i) {
1330
            if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
1331
                ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
1332
                $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
1333
            }
1334
        }
1335
1336
        return $result;
1337
    }
1338
1339
    /**
1340
     * SUMX2PY2
1341
     *
1342
     * @param    mixed[]    $matrixData1    Matrix #1
1343
     * @param    mixed[]    $matrixData2    Matrix #2
1344
     * @return    float
1345
     */
1346 View Code Duplication
    public static function SUMX2PY2($matrixData1, $matrixData2)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1347
    {
1348
        $array1 = Functions::flattenArray($matrixData1);
1349
        $array2 = Functions::flattenArray($matrixData2);
1350
        $count = min(count($array1), count($array2));
1351
1352
        $result = 0;
1353
        for ($i = 0; $i < $count; ++$i) {
1354
            if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
1355
                ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
1356
                $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
1357
            }
1358
        }
1359
1360
        return $result;
1361
    }
1362
1363
    /**
1364
     * SUMXMY2
1365
     *
1366
     * @param    mixed[]    $matrixData1    Matrix #1
1367
     * @param    mixed[]    $matrixData2    Matrix #2
1368
     * @return    float
1369
     */
1370 View Code Duplication
    public static function SUMXMY2($matrixData1, $matrixData2)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1371
    {
1372
        $array1 = Functions::flattenArray($matrixData1);
1373
        $array2 = Functions::flattenArray($matrixData2);
1374
        $count = min(count($array1), count($array2));
1375
1376
        $result = 0;
1377
        for ($i = 0; $i < $count; ++$i) {
1378
            if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
1379
                ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
1380
                $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
1381
            }
1382
        }
1383
1384
        return $result;
1385
    }
1386
1387
    /**
1388
     * TRUNC
1389
     *
1390
     * Truncates value to the number of fractional digits by number_digits.
1391
     *
1392
     * @param    float        $value
1393
     * @param    int            $digits
1394
     * @return    float        Truncated value
1395
     */
1396 19
    public static function TRUNC($value = 0, $digits = 0)
1397
    {
1398 19
        $value = Functions::flattenSingleValue($value);
1399 19
        $digits = Functions::flattenSingleValue($digits);
1400
1401
        // Validate parameters
1402 19
        if ((!is_numeric($value)) || (!is_numeric($digits))) {
1403 2
            return Functions::VALUE();
1404
        }
1405 17
        $digits = floor($digits);
1406
1407
        // Truncate
1408 17
        $adjust = pow(10, $digits);
1409
1410 17
        if (($digits > 0) && (rtrim(intval((abs($value) - abs(intval($value))) * $adjust), '0') < $adjust / 10)) {
1411 2
            return $value;
1412
        }
1413
1414 15
        return (intval($value * $adjust)) / $adjust;
1415
    }
1416
}
1417