Completed
Push — develop ( 685e29...09d456 )
by Adrien
14:14
created

MathTrig::CEILING()   C

Complexity

Conditions 8
Paths 8

Size

Total Lines 22
Code Lines 14

Duplication

Lines 21
Ratio 95.45 %

Code Coverage

Tests 11
CRAP Score 8.0368

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 14
c 1
b 0
f 0
nc 8
nop 2
dl 21
loc 22
ccs 11
cts 12
cp 0.9167
crap 8.0368
rs 6.6037
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 23
49
            return $factorArray;
50 35
        } else {
51
            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
    public static function ATAN2($xCoordinate = null, $yCoordinate = null)
82
    {
83 16
        $xCoordinate = Functions::flattenSingleValue($xCoordinate);
84
        $yCoordinate = Functions::flattenSingleValue($yCoordinate);
85 16
86 16
        $xCoordinate = ($xCoordinate !== null) ? $xCoordinate : 0.0;
87
        $yCoordinate = ($yCoordinate !== null) ? $yCoordinate : 0.0;
88 16
89 16
        if (((is_numeric($xCoordinate)) || (is_bool($xCoordinate))) &&
90
            ((is_numeric($yCoordinate))) || (is_bool($yCoordinate))) {
91 16
            $xCoordinate = (float) $xCoordinate;
92 16
            $yCoordinate = (float) $yCoordinate;
93 15
94 15
            if (($xCoordinate == 0) && ($yCoordinate == 0)) {
95
                return Functions::DIV0();
96 15
            }
97 1
98
            return atan2($yCoordinate, $xCoordinate);
99
        }
100 14
101
        return Functions::VALUE();
102 1
    }
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 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
        $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 43
        }
129 43
130
        if ((is_numeric($number)) && (is_numeric($significance))) {
131
            if (($number == 0.0) || ($significance == 0.0)) {
132
                return 0.0;
133 43
            } elseif (self::SIGN($number) == self::SIGN($significance)) {
134 41
                return ceil($number / $significance) * $significance;
135 3
            } else {
136 38
                return Functions::NAN();
137 35
            }
138
        }
139 3
140
        return Functions::VALUE();
141
    }
142 2
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
    public static function COMBIN($numObjs, $numInSet)
158
    {
159
        $numObjs = Functions::flattenSingleValue($numObjs);
160
        $numInSet = Functions::flattenSingleValue($numInSet);
161 24
162
        if ((is_numeric($numObjs)) && (is_numeric($numInSet))) {
163 24
            if ($numObjs < $numInSet) {
164 24
                return Functions::NAN();
165
            } elseif ($numInSet < 0) {
166 24
                return Functions::NAN();
167 23
            }
168 3
169 20
            return round(self::FACT($numObjs) / self::FACT($numObjs - $numInSet)) / self::FACT($numInSet);
170 2
        }
171
172 18
        return Functions::VALUE();
173
    }
174 1
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
    public static function EVEN($number)
192
    {
193
        $number = Functions::flattenSingleValue($number);
194
195 25
        if (is_null($number)) {
196
            return 0;
197 25
        } elseif (is_bool($number)) {
198
            $number = (int) $number;
199 25
        }
200 1
201 24
        if (is_numeric($number)) {
202 2
            $significance = 2 * self::SIGN($number);
203
204
            return (int) self::CEILING($number, $significance);
205 24
        }
206 23
207 23
        return Functions::VALUE();
208
    }
209 1
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
    public static function FACT($factVal)
224
    {
225
        $factVal = Functions::flattenSingleValue($factVal);
226
227 145
        if (is_numeric($factVal)) {
228
            if ($factVal < 0) {
229 145
                return Functions::NAN();
230
            }
231 145
            $factLoop = floor($factVal);
232 144
            if (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC) {
233 1
                if ($factVal > $factLoop) {
234
                    return Functions::NAN();
235 143
                }
236 143
            }
237
238
            $factorial = 1;
239
            while ($factLoop > 1) {
240
                $factorial *= $factLoop--;
241
            }
242 143
243 143
            return $factorial;
244 71
        }
245
246 143
        return Functions::VALUE();
247
    }
248 1
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
    public static function FACTDOUBLE($factVal)
262
    {
263
        $factLoop = Functions::flattenSingleValue($factVal);
264
265 8
        if (is_numeric($factLoop)) {
266
            $factLoop = floor($factLoop);
267 8
            if ($factVal < 0) {
268
                return Functions::NAN();
269 8
            }
270 7
            $factorial = 1;
271 7
            while ($factLoop > 1) {
272 1
                $factorial *= $factLoop--;
273
                --$factLoop;
274 6
            }
275 6
276 5
            return $factorial;
277 5
        }
278
279 6
        return Functions::VALUE();
280
    }
281 1
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 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
        $number = Functions::flattenSingleValue($number);
298
        $significance = Functions::flattenSingleValue($significance);
299 11
300
        if ((is_null($significance)) &&
301 11
            (Functions::getCompatibilityMode() == Functions::COMPATIBILITY_GNUMERIC)) {
302 11
            $significance = $number / abs($number);
303
        }
304 11
305 11
        if ((is_numeric($number)) && (is_numeric($significance))) {
306
            if ($significance == 0.0) {
307
                return Functions::DIV0();
308
            } elseif ($number == 0.0) {
309 11
                return 0.0;
310 9
            } elseif (self::SIGN($number) == self::SIGN($significance)) {
311 1
                return floor($number / $significance) * $significance;
312 8
            } else {
313
                return Functions::NAN();
314 8
            }
315 6
        }
316
317 2
        return Functions::VALUE();
318
    }
319
320
    /**
321 2
     * 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
    public static function GCD()
335
    {
336
        $returnValue = 1;
337
        $allValuesFactors = [];
338
        // Loop through arguments
339
        foreach (Functions::flattenArray(func_get_args()) as $value) {
340 24
            if (!is_numeric($value)) {
341
                return Functions::VALUE();
342 24
            } elseif ($value == 0) {
343 24
                continue;
344
            } elseif ($value < 0) {
345 24
                return Functions::NAN();
346 24
            }
347 1
            $myFactors = self::factors($value);
348 24
            $myCountedFactors = array_count_values($myFactors);
349 4
            $allValuesFactors[] = $myCountedFactors;
350 23
        }
351 1
        $allValuesCount = count($allValuesFactors);
352
        if ($allValuesCount == 0) {
353 23
            return 0;
354 23
        }
355 23
356
        $mergedArray = $allValuesFactors[0];
357 22
        for ($i = 1; $i < $allValuesCount; ++$i) {
358 22
            $mergedArray = array_intersect_key($mergedArray, $allValuesFactors[$i]);
359 1
        }
360
        $mergedArrayValues = count($mergedArray);
361
        if ($mergedArrayValues == 0) {
362 21
            return $returnValue;
363 21
        } elseif ($mergedArrayValues > 1) {
364 19
            foreach ($mergedArray as $mergedKey => $mergedValue) {
365
                foreach ($allValuesFactors as $highestPowerTest) {
366 21
                    foreach ($highestPowerTest as $testKey => $testValue) {
367 21
                        if (($testKey == $mergedKey) && ($testValue < $mergedValue)) {
368 6
                            $mergedArray[$mergedKey] = $testValue;
369 15
                            $mergedValue = $testValue;
370 3
                        }
371 3
                    }
372 3
                }
373 3
            }
374 2
375 3
            $returnValue = 1;
376
            foreach ($mergedArray as $key => $value) {
377
                $returnValue *= pow($key, $value);
378
            }
379
380
            return $returnValue;
381 3
        } else {
382 3
            $keys = array_keys($mergedArray);
383 3
            $key = $keys[0];
384
            $value = $mergedArray[$key];
385 3
            foreach ($allValuesFactors as $testValue) {
386
                foreach ($testValue as $mergedKey => $mergedValue) {
387 12
                    if (($mergedKey == $key) && ($mergedValue < $value)) {
388 12
                        $value = $mergedValue;
389 12
                    }
390 12
                }
391 12
            }
392 12
393 12
            return pow($key, $value);
394
        }
395
    }
396
397 12
    /**
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 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
        $number = Functions::flattenSingleValue($number);
412
413
        if (is_null($number)) {
414
            return 0;
415 19
        } elseif (is_bool($number)) {
416
            return (int) $number;
417 19
        }
418
        if (is_numeric($number)) {
419 19
            return (int) floor($number);
420 1
        }
421 18
422 2
        return Functions::VALUE();
423
    }
424 16
425 15
    /**
426
     * LCM
427 1
     *
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
    public static function LCM()
441
    {
442
        $returnValue = 1;
443
        $allPoweredFactors = [];
444
        // Loop through arguments
445
        foreach (Functions::flattenArray(func_get_args()) as $value) {
446
            if (!is_numeric($value)) {
447 12
                return Functions::VALUE();
448
            }
449 12
            if ($value == 0) {
450 12
                return 0;
451
            } elseif ($value < 0) {
452 12
                return Functions::NAN();
453 12
            }
454 1
            $myFactors = self::factors(floor($value));
455
            $myCountedFactors = array_count_values($myFactors);
456 12
            $myPoweredFactors = [];
457 1
            foreach ($myCountedFactors as $myCountedFactor => $myCountedPower) {
458 12
                $myPoweredFactors[$myCountedFactor] = pow($myCountedFactor, $myCountedPower);
459 1
            }
460
            foreach ($myPoweredFactors as $myPoweredValue => $myPoweredFactor) {
461 12
                if (array_key_exists($myPoweredValue, $allPoweredFactors)) {
462 12
                    if ($allPoweredFactors[$myPoweredValue] < $myPoweredFactor) {
463 12
                        $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
464 12
                    }
465 12
                } else {
466
                    $allPoweredFactors[$myPoweredValue] = $myPoweredFactor;
467 12
                }
468 12
            }
469 6
        }
470 6
        foreach ($allPoweredFactors as $allPoweredFactor) {
471
            $returnValue *= (integer) $allPoweredFactor;
472
        }
473 12
474
        return $returnValue;
475
    }
476
477 9
    /**
478 9
     * LOG_BASE
479
     *
480 9
     * 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
    public static function logBase($number = null, $base = 10)
491
    {
492
        $number = Functions::flattenSingleValue($number);
493
        $base = (is_null($base)) ? 10 : (float) Functions::flattenSingleValue($base);
494
495
        if ((!is_numeric($base)) || (!is_numeric($number))) {
496
            return Functions::VALUE();
497
        }
498 69
        if (($base <= 0) || ($number <= 0)) {
499
            return Functions::NAN();
500 69
        }
501 69
502
        return log($number, $base);
503 69
    }
504 2
505
    /**
506 67
     * MDETERM
507 18
     *
508
     * Returns the matrix determinant of an array.
509 49
     *
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 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
        $matrixData = [];
520
        if (!is_array($matrixValues)) {
521
            $matrixValues = [[$matrixValues]];
522
        }
523
524
        $row = $maxColumn = 0;
525
        foreach ($matrixValues as $matrixRow) {
526
            if (!is_array($matrixRow)) {
527
                $matrixRow = [$matrixRow];
528
            }
529
            $column = 0;
530
            foreach ($matrixRow as $matrixCell) {
531
                if ((is_string($matrixCell)) || ($matrixCell === null)) {
532
                    return Functions::VALUE();
533
                }
534
                $matrixData[$column][$row] = $matrixCell;
535
                ++$column;
536
            }
537
            if ($column > $maxColumn) {
538
                $maxColumn = $column;
539
            }
540
            ++$row;
541
        }
542
        if ($row != $maxColumn) {
543
            return Functions::VALUE();
544
        }
545
546
        try {
547
            $matrix = new \PhpSpreadsheet\Shared\JAMA\Matrix($matrixData);
548
549
            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
            var_dump($ex->getMessage());
0 ignored issues
show
Security Debugging Code introduced by
var_dump($ex->getMessage()); looks like debug code. Are you sure you do not want to remove it? This might expose sensitive data.
Loading history...
665
666
            return Functions::VALUE();
667
        }
668
    }
669
670
    /**
671
     * MOD
672
     *
673
     * @param    int        $a        Dividend
674
     * @param    int        $b        Divisor
675
     * @return    int        Remainder
676
     */
677
    public static function MOD($a = 1, $b = 1)
678
    {
679
        $a = Functions::flattenSingleValue($a);
680
        $b = Functions::flattenSingleValue($b);
681
682
        if ($b == 0.0) {
683
            return Functions::DIV0();
684
        } elseif (($a < 0.0) && ($b > 0.0)) {
685
            return $b - fmod(abs($a), $b);
686
        } elseif (($a > 0.0) && ($b < 0.0)) {
687 9
            return $b + fmod($a, abs($b));
688
        }
689 9
690 9
        return fmod($a, $b);
691
    }
692 9
693 1
    /**
694 8
     * MROUND
695 1
     *
696 7
     * Rounds a number to the nearest multiple of a specified value
697 2
     *
698
     * @param    float    $number            Number to round
699
     * @param    int        $multiple        Multiple to which you want to round $number
700 5
     * @return    float    Rounded Number
701
     */
702
    public static function MROUND($number, $multiple)
703
    {
704
        $number = Functions::flattenSingleValue($number);
705
        $multiple = Functions::flattenSingleValue($multiple);
706
707
        if ((is_numeric($number)) && (is_numeric($multiple))) {
708
            if ($multiple == 0) {
709
                return 0;
710
            }
711
            if ((self::SIGN($number)) == (self::SIGN($multiple))) {
712
                $multiplier = 1 / $multiple;
713 13
714
                return round($number * $multiplier) / $multiplier;
715 13
            }
716 13
717
            return Functions::NAN();
718 13
        }
719 11
720 1
        return Functions::VALUE();
721
    }
722 10
723 9
    /**
724 9
     * MULTINOMIAL
725
     *
726 1
     * Returns the ratio of the factorial of a sum of values to the product of factorials.
727
     *
728 2
     * @param    array of mixed        Data Series
729
     * @return    float
730
     */
731
    public static function MULTINOMIAL()
732
    {
733
        $summer = 0;
734
        $divisor = 1;
735
        // Loop through arguments
736
        foreach (Functions::flattenArray(func_get_args()) as $arg) {
737
            // Is it a numeric value?
738
            if (is_numeric($arg)) {
739
                if ($arg < 1) {
740 2
                    return Functions::NAN();
741
                }
742 2
                $summer += floor($arg);
743 2
                $divisor *= self::FACT($arg);
744
            } else {
745 2
                return Functions::VALUE();
746
            }
747 2
        }
748 2
749
        // Return
750
        if ($summer > 0) {
751 2
            $summer = self::FACT($summer);
752 2
753
            return $summer / $divisor;
754 2
        }
755
756
        return 0;
757
    }
758
759 2
    /**
760 2
     * ODD
761 2
     *
762
     * Returns number rounded up to the nearest odd integer.
763
     *
764
     * @param    float    $number            Number to round
765
     * @return    int        Rounded Number
766
     */
767
    public static function ODD($number)
768
    {
769
        $number = Functions::flattenSingleValue($number);
770
771
        if (is_null($number)) {
772
            return 1;
773
        } elseif (is_bool($number)) {
774
            return 1;
775 13
        } elseif (is_numeric($number)) {
776
            $significance = self::SIGN($number);
777 13
            if ($significance == 0) {
778
                return 1;
779 13
            }
780 1
781 12
            $result = self::CEILING($number, $significance);
782 2
            if ($result == self::EVEN($result)) {
783 10
                $result += $significance;
784 9
            }
785 9
786 1
            return (int) $result;
787
        }
788
789 8
        return Functions::VALUE();
790 8
    }
791 5
792
    /**
793
     * POWER
794 8
     *
795
     * Computes x raised to the power y.
796 1
     *
797
     * @param    float        $x
798
     * @param    float        $y
799
     * @return    float
800
     */
801
    public static function POWER($x = 0, $y = 2)
802
    {
803
        $x = Functions::flattenSingleValue($x);
804
        $y = Functions::flattenSingleValue($y);
805
806
        // Validate parameters
807
        if ($x == 0.0 && $y == 0.0) {
808
            return Functions::NAN();
809 81
        } elseif ($x == 0.0 && $y < 0.0) {
810
            return Functions::DIV0();
811 81
        }
812 81
813
        // Return
814
        $result = pow($x, $y);
815 81
816 1
        return (!is_nan($result) && !is_infinite($result)) ? $result : Functions::NAN();
817 80
    }
818 2
819
    /**
820
     * PRODUCT
821
     *
822 78
     * PRODUCT returns the product of all the values and cells referenced in the argument list.
823 78
     *
824
     * Excel Function:
825
     *        PRODUCT(value1[,value2[, ...]])
826
     *
827
     * @category Mathematical and Trigonometric Functions
828
     * @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...
829
     * @return    float
830
     */
831 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...
832
    {
833
        // Return value
834
        $returnValue = null;
835
836
        // Loop through arguments
837
        foreach (Functions::flattenArray(func_get_args()) as $arg) {
838
            // Is it a numeric value?
839
            if ((is_numeric($arg)) && (!is_string($arg))) {
840 7
                if (is_null($returnValue)) {
841
                    $returnValue = $arg;
842
                } else {
843 7
                    $returnValue *= $arg;
844
                }
845
            }
846 7
        }
847
848 7
        // Return
849 7
        if (is_null($returnValue)) {
850 7
            return 0;
851
        }
852 7
853
        return $returnValue;
854
    }
855
856
    /**
857
     * QUOTIENT
858 7
     *
859
     * QUOTIENT function returns the integer portion of a division. Numerator is the divided number
860
     *        and denominator is the divisor.
861 7
     *
862
     * Excel Function:
863
     *        QUOTIENT(value1[,value2[, ...]])
864
     *
865
     * @category Mathematical and Trigonometric Functions
866
     * @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...
867
     * @return    float
868
     */
869
    public static function QUOTIENT()
870
    {
871
        // Return value
872
        $returnValue = null;
873
874
        // Loop through arguments
875
        foreach (Functions::flattenArray(func_get_args()) as $arg) {
876
            // Is it a numeric value?
877
            if ((is_numeric($arg)) && (!is_string($arg))) {
878
                if (is_null($returnValue)) {
879 6
                    $returnValue = ($arg == 0) ? 0 : $arg;
880
                } else {
881
                    if (($returnValue == 0) || ($arg == 0)) {
882 6
                        $returnValue = 0;
883
                    } else {
884
                        $returnValue /= $arg;
885 6
                    }
886
                }
887 6
            }
888 6
        }
889 6
890
        // Return
891 6
        return intval($returnValue);
892
    }
893
894 6
    /**
895
     * RAND
896
     *
897
     * @param    int        $min    Minimal value
898
     * @param    int        $max    Maximal value
899
     * @return    int        Random number
900
     */
901 6
    public static function RAND($min = 0, $max = 0)
902
    {
903
        $min = Functions::flattenSingleValue($min);
904
        $max = Functions::flattenSingleValue($max);
905
906
        if ($min == 0 && $max == 0) {
907
            return (mt_rand(0, 10000000)) / 10000000;
908
        } else {
909
            return mt_rand($min, $max);
910
        }
911
    }
912
913
    public static function ROMAN($aValue, $style = 0)
914
    {
915
        $aValue = Functions::flattenSingleValue($aValue);
916
        $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...
917 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...
918
            return Functions::VALUE();
919
        }
920
        $aValue = (integer) $aValue;
921
        if ($aValue == 0) {
922
            return '';
923
        }
924
925 5
        $mill = ['', 'M', 'MM', 'MMM', 'MMMM', 'MMMMM'];
926
        $cent = ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'];
927 5
        $tens = ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'];
928 5
        $ones = ['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX'];
929 5
930
        $roman = '';
931
        while ($aValue > 5999) {
932 5
            $roman .= 'M';
933 5
            $aValue -= 1000;
934
        }
935
        $m = self::romanCut($aValue, 1000);
936
        $aValue %= 1000;
937 5
        $c = self::romanCut($aValue, 100);
938 5
        $aValue %= 100;
939 5
        $t = self::romanCut($aValue, 10);
940 5
        $aValue %= 10;
941
942 5
        return $roman . $mill[$m] . $cent[$c] . $tens[$t] . $ones[$aValue];
943 5
    }
944
945
    /**
946
     * ROUNDUP
947 5
     *
948 5
     * Rounds a number up to a specified number of decimal places
949 5
     *
950 5
     * @param    float    $number            Number to round
951 5
     * @param    int        $digits            Number of digits to which you want to round $number
952 5
     * @return    float    Rounded Number
953
     */
954 5 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...
955
    {
956
        $number = Functions::flattenSingleValue($number);
957
        $digits = Functions::flattenSingleValue($digits);
958
959
        if ((is_numeric($number)) && (is_numeric($digits))) {
960
            $significance = pow(10, (int) $digits);
961
            if ($number < 0.0) {
962
                return floor($number * $significance) / $significance;
963
            } else {
964
                return ceil($number * $significance) / $significance;
965
            }
966
        }
967 14
968
        return Functions::VALUE();
969 14
    }
970 14
971
    /**
972 14
     * ROUNDDOWN
973 12
     *
974 12
     * Rounds a number down to a specified number of decimal places
975 2
     *
976
     * @param    float    $number            Number to round
977 10
     * @param    int        $digits            Number of digits to which you want to round $number
978
     * @return    float    Rounded Number
979
     */
980 2 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...
981
    {
982
        $number = Functions::flattenSingleValue($number);
983
        $digits = Functions::flattenSingleValue($digits);
984
985
        if ((is_numeric($number)) && (is_numeric($digits))) {
986
            $significance = pow(10, (int) $digits);
987
            if ($number < 0.0) {
988
                return ceil($number * $significance) / $significance;
989
            } else {
990
                return floor($number * $significance) / $significance;
991
            }
992
        }
993 14
994
        return Functions::VALUE();
995 14
    }
996 14
997
    /**
998 14
     * SERIESSUM
999 12
     *
1000 12
     * Returns the sum of a power series
1001 2
     *
1002
     * @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...
1003 10
     * @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...
1004
     * @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...
1005
     * @param    array of mixed        Data Series
1006 2
     * @return    float
1007
     */
1008
    public static function SERIESSUM()
1009
    {
1010
        $returnValue = 0;
1011
1012
        // Loop through arguments
1013
        $aArgs = Functions::flattenArray(func_get_args());
1014
1015
        $x = array_shift($aArgs);
1016
        $n = array_shift($aArgs);
1017
        $m = array_shift($aArgs);
1018
1019
        if ((is_numeric($x)) && (is_numeric($n)) && (is_numeric($m))) {
1020
            // Calculate
1021 2
            $i = 0;
1022
            foreach ($aArgs as $arg) {
1023 2
                // Is it a numeric value?
1024
                if ((is_numeric($arg)) && (!is_string($arg))) {
1025
                    $returnValue += $arg * pow($x, $n + ($m * $i++));
1026 2
                } else {
1027
                    return Functions::VALUE();
1028 2
                }
1029 2
            }
1030 2
1031
            return $returnValue;
1032 2
        }
1033
1034 2
        return Functions::VALUE();
1035 2
    }
1036
1037 2
    /**
1038 2
     * SIGN
1039
     *
1040 2
     * Determines the sign of a number. Returns 1 if the number is positive, zero (0)
1041
     *        if the number is 0, and -1 if the number is negative.
1042
     *
1043 2
     * @param    float    $number            Number to round
1044
     * @return    int        sign value
1045
     */
1046 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...
1047
    {
1048
        $number = Functions::flattenSingleValue($number);
1049
1050
        if (is_bool($number)) {
1051
            return (int) $number;
1052
        }
1053
        if (is_numeric($number)) {
1054
            if ($number == 0.0) {
1055
                return 0;
1056
            }
1057
1058 72
            return $number / abs($number);
1059
        }
1060 72
1061
        return Functions::VALUE();
1062 72
    }
1063 2
1064
    /**
1065 70
     * SQRTPI
1066 69
     *
1067 4
     * Returns the square root of (number * pi).
1068
     *
1069 65
     * @param    float    $number        Number
1070
     * @return    float    Square Root of Number * Pi
1071 1
     */
1072 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...
1073
    {
1074
        $number = Functions::flattenSingleValue($number);
1075
1076
        if (is_numeric($number)) {
1077
            if ($number < 0) {
1078
                return Functions::NAN();
1079
            }
1080
1081
            return sqrt($number * M_PI);
1082
        }
1083 15
1084
        return Functions::VALUE();
1085 15
    }
1086
1087 15
    /**
1088 14
     * SUBTOTAL
1089 3
     *
1090
     * Returns a subtotal in a list or database.
1091 11
     *
1092
     * @param    int        the number 1 to 11 that specifies which function to
1093 1
     *                    use in calculating subtotals within a list.
1094
     * @param    array of mixed        Data Series
1095
     * @return    float
1096
     */
1097
    public static function SUBTOTAL()
1098
    {
1099
        $aArgs = Functions::flattenArray(func_get_args());
1100
1101
        // Calculate
1102
        $subtotal = array_shift($aArgs);
1103
1104
        if ((is_numeric($subtotal)) && (!is_string($subtotal))) {
1105
            switch ($subtotal) {
1106
                case 1:
1107
                    return Statistical::AVERAGE($aArgs);
1108
                case 2:
1109
                    return Statistical::COUNT($aArgs);
1110
                case 3:
1111
                    return Statistical::COUNTA($aArgs);
1112
                case 4:
1113
                    return Statistical::MAX($aArgs);
1114
                case 5:
1115
                    return Statistical::MIN($aArgs);
1116
                case 6:
1117
                    return self::PRODUCT($aArgs);
1118
                case 7:
1119
                    return Statistical::STDEV($aArgs);
1120
                case 8:
1121
                    return Statistical::STDEVP($aArgs);
1122
                case 9:
1123
                    return self::SUM($aArgs);
1124
                case 10:
1125
                    return Statistical::VARFunc($aArgs);
1126
                case 11:
1127
                    return Statistical::VARP($aArgs);
1128
            }
1129
        }
1130
1131
        return Functions::VALUE();
1132
    }
1133
1134
    /**
1135
     * SUM
1136
     *
1137
     * SUM computes the sum of all the values and cells referenced in the argument list.
1138
     *
1139
     * Excel Function:
1140
     *        SUM(value1[,value2[, ...]])
1141
     *
1142
     * @category Mathematical and Trigonometric Functions
1143
     * @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...
1144
     * @return    float
1145
     */
1146 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...
1147
    {
1148
        $returnValue = 0;
1149
1150
        // Loop through the arguments
1151
        foreach (Functions::flattenArray(func_get_args()) as $arg) {
1152
            // Is it a numeric value?
1153
            if ((is_numeric($arg)) && (!is_string($arg))) {
1154
                $returnValue += $arg;
1155
            }
1156
        }
1157
1158
        return $returnValue;
1159
    }
1160
1161
    /**
1162
     * SUMIF
1163
     *
1164
     * Counts the number of cells that contain numbers within the list of arguments
1165
     *
1166
     * Excel Function:
1167
     *        SUMIF(value1[,value2[, ...]],condition)
1168
     *
1169
     * @category Mathematical and Trigonometric Functions
1170
     * @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...
1171
     * @param    string        $condition        The criteria that defines which cells will be summed.
1172
     * @return    float
1173
     */
1174
    public static function SUMIF($aArgs, $condition, $sumArgs = [])
1175
    {
1176
        $returnValue = 0;
1177
1178
        $aArgs = Functions::flattenArray($aArgs);
1179
        $sumArgs = Functions::flattenArray($sumArgs);
1180
        if (empty($sumArgs)) {
1181
            $sumArgs = $aArgs;
1182
        }
1183
        $condition = Functions::ifCondition($condition);
1184
        // Loop through arguments
1185
        foreach ($aArgs as $key => $arg) {
1186
            if (!is_numeric($arg)) {
1187 5
                $arg = str_replace('"', '""', $arg);
1188
                $arg = \PhpSpreadsheet\Calculation::wrapResult(strtoupper($arg));
1189 5
            }
1190
1191 5
            $testCondition = '=' . $arg . $condition;
1192 5
            if (\PhpSpreadsheet\Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
1193 5
                // Is it a value within our criteria
1194 1
                $returnValue += $sumArgs[$key];
1195
            }
1196 5
        }
1197
1198 5
        return $returnValue;
1199 5
    }
1200 4
1201 4
    /**
1202
     *    SUMIFS
1203
     *
1204 5
     *    Counts the number of cells that contain numbers within the list of arguments
1205 5
     *
1206
     *    Excel Function:
1207 5
     *        SUMIFS(value1[,value2[, ...]],condition)
1208
     *
1209
     *    @category Mathematical and Trigonometric Functions
1210
     *    @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...
1211 5
     *    @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...
1212
     *    @return    float
1213
     */
1214
    public static function SUMIFS()
1215
    {
1216
        $arrayList = func_get_args();
1217
1218
        // Return value
1219
        $returnValue = 0;
1220
1221
        $sumArgs = Functions::flattenArray(array_shift($arrayList));
1222
1223
        while (count($arrayList) > 0) {
1224
            $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...
1225
            $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...
1226
        }
1227
1228
        // Loop through each set of arguments and conditions
1229
        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...
1230
            $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...
1231
1232
            // Loop through arguments
1233
            foreach ($aArgs as $key => $arg) {
1234
                if (!is_numeric($arg)) {
1235
                    $arg = \PhpSpreadsheet\Calculation::wrapResult(strtoupper($arg));
1236
                }
1237
                $testCondition = '=' . $arg . $condition;
1238
                if (\PhpSpreadsheet\Calculation::getInstance()->_calculateFormulaValue($testCondition)) {
1239
                    // Is it a value within our criteria
1240
                    $returnValue += $sumArgs[$key];
1241
                }
1242
            }
1243
        }
1244
1245
        // Return
1246
        return $returnValue;
1247
    }
1248
1249
    /**
1250
     * SUMPRODUCT
1251
     *
1252
     * Excel Function:
1253
     *        SUMPRODUCT(value1[,value2[, ...]])
1254
     *
1255
     * @category Mathematical and Trigonometric Functions
1256
     * @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...
1257
     * @return    float
1258
     */
1259
    public static function SUMPRODUCT()
1260
    {
1261
        $arrayList = func_get_args();
1262
1263
        $wrkArray = Functions::flattenArray(array_shift($arrayList));
1264
        $wrkCellCount = count($wrkArray);
1265
1266
        for ($i = 0; $i < $wrkCellCount; ++$i) {
1267
            if ((!is_numeric($wrkArray[$i])) || (is_string($wrkArray[$i]))) {
1268
                $wrkArray[$i] = 0;
1269
            }
1270
        }
1271
1272
        foreach ($arrayList as $matrixData) {
1273
            $array2 = Functions::flattenArray($matrixData);
1274
            $count = count($array2);
1275
            if ($wrkCellCount != $count) {
1276
                return Functions::VALUE();
1277
            }
1278
1279
            foreach ($array2 as $i => $val) {
1280
                if ((!is_numeric($val)) || (is_string($val))) {
1281
                    $val = 0;
1282
                }
1283
                $wrkArray[$i] *= $val;
1284
            }
1285
        }
1286
1287
        return array_sum($wrkArray);
1288
    }
1289
1290
    /**
1291
     * SUMSQ
1292
     *
1293
     * SUMSQ returns the sum of the squares of the arguments
1294
     *
1295
     * Excel Function:
1296
     *        SUMSQ(value1[,value2[, ...]])
1297
     *
1298
     * @category Mathematical and Trigonometric Functions
1299
     * @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...
1300
     * @return    float
1301
     */
1302 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...
1303
    {
1304
        $returnValue = 0;
1305
1306
        // Loop through arguments
1307
        foreach (Functions::flattenArray(func_get_args()) as $arg) {
1308
            // Is it a numeric value?
1309
            if ((is_numeric($arg)) && (!is_string($arg))) {
1310
                $returnValue += ($arg * $arg);
1311
            }
1312
        }
1313
1314
        return $returnValue;
1315
    }
1316
1317
    /**
1318
     * SUMX2MY2
1319 7
     *
1320
     * @param    mixed[]    $matrixData1    Matrix #1
1321 7
     * @param    mixed[]    $matrixData2    Matrix #2
1322
     * @return    float
1323
     */
1324 7 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...
1325
    {
1326 7
        $array1 = Functions::flattenArray($matrixData1);
1327 7
        $array2 = Functions::flattenArray($matrixData2);
1328
        $count = min(count($array1), count($array2));
1329
1330
        $result = 0;
1331 7
        for ($i = 0; $i < $count; ++$i) {
1332
            if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
1333
                ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
1334
                $result += ($array1[$i] * $array1[$i]) - ($array2[$i] * $array2[$i]);
1335
            }
1336
        }
1337
1338
        return $result;
1339
    }
1340
1341
    /**
1342
     * SUMX2PY2
1343
     *
1344
     * @param    mixed[]    $matrixData1    Matrix #1
1345
     * @param    mixed[]    $matrixData2    Matrix #2
1346
     * @return    float
1347
     */
1348 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...
1349
    {
1350
        $array1 = Functions::flattenArray($matrixData1);
1351
        $array2 = Functions::flattenArray($matrixData2);
1352
        $count = min(count($array1), count($array2));
1353
1354
        $result = 0;
1355
        for ($i = 0; $i < $count; ++$i) {
1356
            if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
1357
                ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
1358
                $result += ($array1[$i] * $array1[$i]) + ($array2[$i] * $array2[$i]);
1359
            }
1360
        }
1361
1362
        return $result;
1363
    }
1364
1365
    /**
1366
     * SUMXMY2
1367
     *
1368
     * @param    mixed[]    $matrixData1    Matrix #1
1369
     * @param    mixed[]    $matrixData2    Matrix #2
1370
     * @return    float
1371
     */
1372 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...
1373
    {
1374
        $array1 = Functions::flattenArray($matrixData1);
1375
        $array2 = Functions::flattenArray($matrixData2);
1376
        $count = min(count($array1), count($array2));
1377
1378
        $result = 0;
1379
        for ($i = 0; $i < $count; ++$i) {
1380
            if (((is_numeric($array1[$i])) && (!is_string($array1[$i]))) &&
1381
                ((is_numeric($array2[$i])) && (!is_string($array2[$i])))) {
1382
                $result += ($array1[$i] - $array2[$i]) * ($array1[$i] - $array2[$i]);
1383
            }
1384
        }
1385
1386
        return $result;
1387
    }
1388
1389
    /**
1390
     * TRUNC
1391
     *
1392
     * Truncates value to the number of fractional digits by number_digits.
1393
     *
1394
     * @param    float        $value
1395
     * @param    int            $digits
1396
     * @return    float        Truncated value
1397
     */
1398
    public static function TRUNC($value = 0, $digits = 0)
1399
    {
1400
        $value = Functions::flattenSingleValue($value);
1401
        $digits = Functions::flattenSingleValue($digits);
1402
1403
        // Validate parameters
1404
        if ((!is_numeric($value)) || (!is_numeric($digits))) {
1405
            return Functions::VALUE();
1406
        }
1407
        $digits = floor($digits);
1408
1409
        // Truncate
1410
        $adjust = pow(10, $digits);
1411
1412
        if (($digits > 0) && (rtrim(intval((abs($value) - abs(intval($value))) * $adjust), '0') < $adjust / 10)) {
1413
            return $value;
1414
        }
1415
1416
        return (intval($value * $adjust)) / $adjust;
1417
    }
1418
}
1419