Failed Conditions
Push — master ( bf4629...7712d5 )
by Adrien
27:59 queued 18:08
created

BinaryComparison   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 128
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 43
eloc 55
c 0
b 0
f 0
dl 0
loc 128
ccs 61
cts 61
cp 1
rs 8.96

10 Methods

Rating   Name   Duplication   Size   Complexity  
A strcmpAllowNull() 0 3 1
A strcmpLowercaseFirst() 0 6 1
A lessThan() 0 3 1
A greaterThan() 0 3 1
B equal() 0 11 7
A notEqual() 0 3 1
B lessThanOrEqual() 0 13 9
B greaterThanOrEqual() 0 13 9
A evaluateComparison() 0 10 1
C compare() 0 25 12

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
3
namespace PhpOffice\PhpSpreadsheet\Calculation;
4
5
use PhpOffice\PhpSpreadsheet\Shared\StringHelper;
6
7
class BinaryComparison
8
{
9
    /**
10
     * Epsilon Precision used for comparisons in calculations.
11
     */
12
    private const DELTA = 0.1e-12;
13
14
    /**
15
     * Compare two strings in the same way as strcmp() except that lowercase come before uppercase letters.
16
     *
17
     * @param null|string $str1 First string value for the comparison
18
     * @param null|string $str2 Second string value for the comparison
19
     */
20 21
    private static function strcmpLowercaseFirst($str1, $str2): int
21
    {
22 21
        $inversedStr1 = StringHelper::strCaseReverse($str1 ?? '');
23 21
        $inversedStr2 = StringHelper::strCaseReverse($str2 ?? '');
24
25 21
        return strcmp($inversedStr1, $inversedStr2);
26
    }
27
28
    /**
29
     * PHP8.1 deprecates passing null to strcmp.
30
     *
31
     * @param null|string $str1 First string value for the comparison
32
     * @param null|string $str2 Second string value for the comparison
33
     */
34 270
    private static function strcmpAllowNull($str1, $str2): int
35
    {
36 270
        return strcmp($str1 ?? '', $str2 ?? '');
37
    }
38
39 386
    public static function compare(mixed $operand1, mixed $operand2, string $operator): bool
40
    {
41
        //    Simple validate the two operands if they are string values
42 386
        if (is_string($operand1) && $operand1 > '' && $operand1[0] == Calculation::FORMULA_STRING_QUOTE) {
43 226
            $operand1 = Calculation::unwrapResult($operand1);
44
        }
45 386
        if (is_string($operand2) && $operand2 > '' && $operand2[0] == Calculation::FORMULA_STRING_QUOTE) {
46 253
            $operand2 = Calculation::unwrapResult($operand2);
47
        }
48
49
        // Use case insensitive comparaison if not OpenOffice mode
50 386
        if (Functions::getCompatibilityMode() != Functions::COMPATIBILITY_OPENOFFICE) {
51 386
            if (is_string($operand1)) {
52 239
                $operand1 = StringHelper::strToUpper($operand1);
53
            }
54 386
            if (is_string($operand2)) {
55 255
                $operand2 = StringHelper::strToUpper($operand2);
56
            }
57
        }
58
59 386
        $useLowercaseFirstComparison = is_string($operand1) &&
60 386
            is_string($operand2) &&
61 386
            Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE;
62
63 386
        return self::evaluateComparison($operand1, $operand2, $operator, $useLowercaseFirstComparison);
64
    }
65
66 386
    private static function evaluateComparison(mixed $operand1, mixed $operand2, string $operator, bool $useLowercaseFirstComparison): bool
67
    {
68 386
        return match ($operator) {
69 386
            '=' => self::equal($operand1, $operand2),
70 386
            '>' => self::greaterThan($operand1, $operand2, $useLowercaseFirstComparison),
71 386
            '<' => self::lessThan($operand1, $operand2, $useLowercaseFirstComparison),
72 386
            '>=' => self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison),
73 386
            '<=' => self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison),
74 386
            '<>' => self::notEqual($operand1, $operand2),
75 386
            default => throw new Exception('Unsupported binary comparison operator'),
76 386
        };
77
    }
78
79 301
    private static function equal(mixed $operand1, mixed $operand2): bool
80
    {
81 301
        if (is_numeric($operand1) && is_numeric($operand2)) {
82 84
            $result = (abs($operand1 - $operand2) < self::DELTA);
83 248
        } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
84 6
            $result = $operand1 == $operand2;
85
        } else {
86 244
            $result = self::strcmpAllowNull($operand1, $operand2) == 0;
87
        }
88
89 301
        return $result;
90
    }
91
92 83
    private static function greaterThanOrEqual(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
93
    {
94 83
        if (is_numeric($operand1) && is_numeric($operand2)) {
95 73
            $result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 > $operand2));
96 11
        } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
97 2
            $result = $operand1 >= $operand2;
98 9
        } elseif ($useLowercaseFirstComparison) {
99 8
            $result = self::strcmpLowercaseFirst($operand1, $operand2) >= 0;
100
        } else {
101 9
            $result = self::strcmpAllowNull($operand1, $operand2) >= 0;
102
        }
103
104 83
        return $result;
105
    }
106
107 134
    private static function lessThanOrEqual(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
108
    {
109 134
        if (is_numeric($operand1) && is_numeric($operand2)) {
110 117
            $result = ((abs($operand1 - $operand2) < self::DELTA) || ($operand1 < $operand2));
111 24
        } elseif (($operand1 === null && is_numeric($operand2)) || ($operand2 === null && is_numeric($operand1))) {
112 5
            $result = $operand1 <= $operand2;
113 21
        } elseif ($useLowercaseFirstComparison) {
114 13
            $result = self::strcmpLowercaseFirst($operand1, $operand2) <= 0;
115
        } else {
116 21
            $result = self::strcmpAllowNull($operand1, $operand2) <= 0;
117
        }
118
119 134
        return $result;
120
    }
121
122 103
    private static function greaterThan(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
123
    {
124 103
        return self::lessThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
125
    }
126
127 59
    private static function lessThan(mixed $operand1, mixed $operand2, bool $useLowercaseFirstComparison): bool
128
    {
129 59
        return self::greaterThanOrEqual($operand1, $operand2, $useLowercaseFirstComparison) !== true;
130
    }
131
132 36
    private static function notEqual(mixed $operand1, mixed $operand2): bool
133
    {
134 36
        return self::equal($operand1, $operand2) !== true;
135
    }
136
}
137