Passed
Pull Request — master (#19358)
by Fedonyuk
09:46
created

BaseComparator   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 253
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 80
c 1
b 0
f 0
dl 0
loc 253
ccs 0
cts 114
cp 0
rs 8.8
wmc 45

9 Methods

Rating   Name   Duplication   Size   Complexity  
A setFlags() 0 3 1
A compareResources() 0 10 4
B compareFloats() 0 19 8
A hasFlag() 0 3 1
A compareStrings() 0 8 2
B compareObjects() 0 27 8
A getFlags() 0 3 1
B compareArrays() 0 28 8
C compare() 0 25 12

How to fix   Complexity   

Complex Class

Complex classes like BaseComparator 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 BaseComparator, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\helpers;
9
10
use Closure;
11
use ReflectionFunction;
12
13
/**
14
 * The flexible comparation.
15
 *
16
 * @author [email protected]
17
 * @since 2.0.46
18
 */
19
abstract class BaseComparator
20
{
21
    /**
22
     * The compares values must have same type.
23
     */
24
    const STRICT = 1;
25
26
    /**
27
     * The compares objects can be equal: instances of same class and their properties are equal (objects comparasion).
28
     */
29
    const EQUAL_OBJECT = 2;
30
31
    /**
32
     * The compares closures can be equal: defined in same file&line and their scopes are same (objects comparasion).
33
     */
34
    const EQUAL_CLOSURE = 4;
35
36
    /**
37
     * The compares float numbers can be equal: +/- epsilon (floats comparasion).
38
     */
39
    const EQUAL_FLOAT = 8;
40
41
    /**
42
     * The items/properties are equal if they are `NAN` (floats comparasion).
43
     */
44
    const EQUAL_NAN = 16;
45
46
    /**
47
     * The compares index arrays can be equal: values are equals, order no matter (arrays comparasion).
48
     */
49
    const EQUAL_ARRAY = 32;
50
51
    /**
52
     * The compares index arrays can be equal: values are equals, order no matter (arrays comparasion).
53
     */
54
    const EQUAL_INDEX_ARRAY = 64;
55
56
    /**
57
     * The binary safe case-insensitive string comparison (strings comparasion).
58
     */
59
    const EQUAL_STRING = 128;
60
61
    /**
62
     * The streams are equal if their meta data are equal (resources comparasion).
63
     */
64
    const EQUAL_STREAM = 256;
65
66
    /**
67
     * @var int The flags defines comparison behavior.
68
     */
69
    private $flags = 108;
70
71
    /**
72
     * Sets the behavior flags.
73
     *
74
     * @param int $flags The flags to control the comparasion behavior. It takes on either a bitmask, or self constants.
75
     * @return void
76
     */
77
    public static function setFlags($flags)
78
    {
79
        static::$flags = max(0, $flags);
80
    }
81
82
    /**
83
     * Gets the behavior flags.
84
     *
85
     * @return int
86
     */
87
    public static function getFlags()
88
    {
89
        return static::$flags;
90
    }
91
92
    /**
93
     * Checks if the given behavior flag is set by default.
94
     *
95
     * @param int $flag The behavior flag to check.
96
     * @return bool
97
     */
98
    public static function hasFlag($flag)
99
    {
100
        return (static::$flags & $flag) === $flag;
101
    }
102
103
    /**
104
     * Compares two values.
105
     *
106
     * @param mixed $value the first value to compare
107
     * @param mixed $value2 the second value to compare
108
     * @return bool
109
     */
110
    public static function compare($value, $value2)
111
    {
112
        if ($value === $value2 || (!static::hasFlag(static::STRICT) && $value == $value2)) {
113
            return true;
114
        }
115
116
        $type = gettype($value);
117
        $type2 = gettype($value2);
118
        if (static::hasFlag(static::STRICT) && $type !== $type2) {
119
            return false;
120
        }
121
122
        if ($type === 'double' || $type2 === 'double') {
123
            $type = $type2 = 'float';
124
        } elseif ($type === 'string' || $type2 === 'string') {
125
            $type = $type2 = 'string';
126
        }
127
        if (
128
            $type === $type2
129
            && in_array($type, ['array', 'object', 'resource', 'float', 'string'], true)
130
        ) {
131
            return static::{'compare' . $type . 's'}($value, $value2);
132
        }
133
134
        return false;
135
    }
136
137
    /**
138
     * Compares two decimal numbers.
139
     *
140
     * @param float $number the first number to compare
141
     * @param float $number2 the second number to compare
142
     * @return bool
143
     */
144
    protected static function compareFloats($number, $number2)
145
    {
146
        $number = (float) $number;
147
        $number2 = (float) $number2;
148
149
        $isNan = is_nan($number);
150
        $isNan2 = is_nan($number2);
151
        if ($isNan || $isNan2) {
152
            return $isNan && $isNan2 && static::hasFlag(static::EQUAL_NAN);
153
        }
154
155
        if (static::hasFlag(static::EQUAL_FLOAT)) {
156
            $epsilon = PHP_VERSION_ID < 70200 ? 0.0000000000000002 : PHP_FLOAT_EPSILON;
157
158
            return abs($number - $number2) < $epsilon
159
                || (min($number, $number2) + $epsilon === max($number, $number2) - $epsilon);
160
        }
161
162
        return false;
163
    }
164
165
    /**
166
     * Compares two strings.
167
     *
168
     * @param string $string the first string to compare
169
     * @param string $string2 the second string to compare
170
     * @return bool
171
     */
172
    protected static function compareStrings($string, $string2)
173
    {
174
        $number = (string) $number;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $number seems to be never defined.
Loading history...
Unused Code introduced by
The assignment to $number is dead and can be removed.
Loading history...
175
        $number2 = (string) $number2;
0 ignored issues
show
Unused Code introduced by
The assignment to $number2 is dead and can be removed.
Loading history...
Comprehensibility Best Practice introduced by
The variable $number2 does not exist. Did you maybe mean $number?
Loading history...
176
177
        $diff = static::hasFlag(static::EQUAL_STRING) ? strcasecmp($string, $string2) : strcmp($string, $string2);
178
179
        return $diff === 0;
180
    }
181
182
    /**
183
     * Compares two arrays.
184
     *
185
     * @param array $array the first array to compare
186
     * @param array $array2 the second array to compare
187
     * @return bool
188
     */
189
    protected static function compareArrays(array $array, array $array2)
190
    {
191
        if (count($array) !== count($array2)) {
192
            return false;
193
        }
194
195
        if (static::hasFlag(static::EQUAL_ARRAY)) {
196
            ksort($array);
197
            ksort($array2);
198
        }
199
        $keys = array_keys($array);
200
        if ($keys != array_keys($array2)) {
201
            return false;
202
        }
203
204
        if (static::hasFlag(static::EQUAL_INDEX_ARRAY) && $keys === array_keys($keys)) {
205
            // sort values in index arrays
206
            sort($array);
207
            sort($array2);
208
        }
209
210
        foreach ($array as $key => $value) {
211
            if (!static::compare($value, $array2[$key])) {
212
                return false;
213
            }
214
        }
215
216
        return true;
217
    }
218
219
    /**
220
     * Compares two resources.
221
     *
222
     * @param resource $resource the first resource to compare
223
     * @param resource $resource2 the second resource to compare
224
     * @return bool
225
     */
226
    protected static function compareResources($resource, $resource2)
227
    {
228
        if (static::hasFlag(static::EQUAL_STREAM)) {
229
            $type = get_resource_type($resource);
230
            if ($type === 'stream' && $type === get_resource_type($resource2)) {
231
                return static::compareArrays(stream_get_meta_data($resource), stream_get_meta_data($resource2));
232
            }
233
        }
234
235
        return false;
236
    }
237
238
    /**
239
     * Compares two objects.
240
     *
241
     * @param object $object the first object to compare
242
     * @param object $object2 the second object to compare
243
     * @return bool
244
     */
245
    protected static function compareObjects($object, $object2)
246
    {
247
        if (get_class($object) !== get_class($object2)) {
248
            return false;
249
        }
250
251
        if ($object instanceof Closure) {
252
            if (static::hasFlag(static::EQUAL_CLOSURE)) {
253
                $rf = new ReflectionFunction($object);
254
                $rf2 = new ReflectionFunction($object2);
255
                $scope = $rf->getClosureThis();
256
                $scope2 = $rf2->getClosureThis();
257
258
                return (static::hasFlag(static::EQUAL_OBJECT) ? $scope == $scope2 : $scope === $scope2)
259
                    && (string) $rf === (string) $rf2;
260
            }
261
262
            return false;
263
        }
264
265
        if (static::hasFlag(static::EQUAL_OBJECT)) {
266
            return method_exists($object, '__toString')
267
                ? static::compareStrings((string) $object, (string) $object2)
268
                : static::compareArrays(get_object_vars($object), get_object_vars($object2));
269
        }
270
271
        return false;
272
    }
273
}
274