Completed
Push — master ( 47d960...e61af3 )
by Peter
03:38
created

Set::detach()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 4
cts 4
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php
2
3
/**
4
 * GpsLab component.
5
 *
6
 * @author    Peter Gribanov <[email protected]>
7
 * @copyright Copyright (c) 2011, Peter Gribanov
8
 * @license   http://opensource.org/licenses/MIT
9
 */
10
11
namespace GpsLab\Component\Enum;
12
13
use GpsLab\Component\Enum\Exception\InvalidSetException;
14
use GpsLab\Component\Enum\Exception\OutOfEnumException;
15
16
class Set
17
{
18
    /**
19
     * @var int
20
     */
21
    private $bit = 0;
22
23
    /**
24
     * @var mixed[][]
25
     */
26
    private static $bits = [];
27
28
    /**
29
     * @var mixed[][]
30
     */
31
    private static $keys = [];
32
33
    /**
34
     * @param array $values
35
     */
36 23
    final public function __construct(array $values = [])
37
    {
38 23
        foreach ($values as $value) {
39 23
            $this->attach($value);
40 23
        }
41 23
    }
42
43
    /**
44
     * @return mixed[]
45
     */
46 5
    final public function values()
47
    {
48 5
        if (!$this->bit) {
49
            return [];
50
        }
51
52 5
        $values = [];
53 5
        foreach (self::$bits[get_called_class()] as $bit => $value) {
54 5
            if ($this->bit & $bit) {
55 5
                $values[] = $value;
56 5
            }
57 5
        }
58
59 5
        return $values;
60
    }
61
62
    /**
63
     * Attach the given value.
64
     *
65
     * @param mixed $value
66
     */
67 23
    final public function attach($value)
68
    {
69 23
        self::validateValue($value);
70
71 23
        $this->bit |= $this->bit($value);
72 23
    }
73
74
    /**
75
     * Detach the given value.
76
     *
77
     * @param mixed $value
78
     */
79 2
    final public function detach($value)
80
    {
81 2
        self::validateValue($value);
82
83 1
        $this->bit ^= $this->bit($value);
84 1
    }
85
86
    /**
87
     * Given value was attached.
88
     *
89
     * @param mixed $value
90
     *
91
     * @return bool
92
     */
93 2
    final public function contains($value)
94
    {
95 2
        self::validateValue($value);
96
97 1
        return (bool) ($this->bit & $this->bit($value));
98
    }
99
100
    /**
101
     * Is value supported.
102
     *
103
     * @param mixed $value
104
     *
105
     * @return bool
106
     */
107 23
    final public static function isValid($value)
108
    {
109 23
        return in_array($value, self::bits(), true);
110
    }
111
112
    /**
113
     * Check if this Set is the same as other.
114
     *
115
     * @param Set $set
116
     *
117
     * @return bool
118
     */
119 3
    final public function equal(Set $set)
120
    {
121 3
        self::validateType($set);
122
123 2
        return $this->bit === $this->bit;
124
    }
125
126
    /**
127
     * Check if this Set is a subset of other.
128
     *
129
     * @param Set $set
130
     *
131
     * @return bool
132
     */
133 2
    final public function subset(Set $set)
134
    {
135 2
        self::validateType($set);
136
137 1
        return ($this->bit & $set->bit) === $this->bit;
138
    }
139
140
    /**
141
     * Check if this Set is a superset of other.
142
     *
143
     * @param Set $set
144
     *
145
     * @return bool
146
     */
147 2
    final public function superset(Set $set)
148
    {
149 2
        self::validateType($set);
150
151 1
        return ($this->bit | $set->bit) === $this->bit;
152
    }
153
154
    /**
155
     * Produce a new set with enum from both this and other (this | other).
156
     *
157
     * @param Set ...$set Other Set(s) of the same enum to produce the union
158
     *
159
     * @return Set
160
     */
161 2 View Code Duplication
    final public function union(Set $set)
2 ignored issues
show
Unused Code introduced by
The parameter $set is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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...
162
    {
163 2
        $bit = $this->bit;
164 2
        foreach (func_get_args() as $set) {
165 2
            self::validateType($set);
166
167 1
            $bit |= $set->bit;
168 1
        }
169
170 1
        $clone = new static();
171 1
        $clone->bit = $bit;
172
173 1
        return $clone;
174
    }
175
176
    /**
177
     * Produce a new set with enum common to both this and other (this & other).
178
     *
179
     * @param Set ...$set Other Set(s) of the same enumeration to produce the union
180
     *
181
     * @return Set
182
     */
183 2 View Code Duplication
    final public function intersect(Set $set)
2 ignored issues
show
Unused Code introduced by
The parameter $set is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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...
184
    {
185 2
        $bit = $this->bit;
186 2
        foreach (func_get_args() as $set) {
187 2
            self::validateType($set);
188
189 1
            $bit &= $set->bit;
190 1
        }
191
192 1
        $clone = new static();
193 1
        $clone->bit = $bit;
194
195 1
        return $clone;
196
    }
197
198
    /**
199
     * Produce a new set with enum in this but not in other (this - other).
200
     *
201
     * @param Set ...$set Other Set(s) of the same enumeration to produce the union
202
     *
203
     * @return Set
204
     */
205 2 View Code Duplication
    final public function diff(Set $set)
2 ignored issues
show
Unused Code introduced by
The parameter $set is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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...
206
    {
207 2
        $bit = 0;
208 2
        foreach (func_get_args() as $set) {
209 2
            self::validateType($set);
210
211 1
            $bit |= $set->bit;
212 1
        }
213
214 1
        $clone = new static();
215 1
        $clone->bit = $this->bit & ~$bit;
216
217 1
        return $clone;
218
    }
219
220
    /**
221
     * Produce a new set with enum in either this and other but not in both (this ^ (other | other)).
222
     *
223
     * @param Set ...$set Other Set(s) of the same enumeration to produce the union
224
     *
225
     * @return Set
226
     */
227 2 View Code Duplication
    final public function symDiff(Set $set)
2 ignored issues
show
Unused Code introduced by
The parameter $set is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
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...
228
    {
229 2
        $bit = 0;
230 2
        foreach (func_get_args() as $set) {
231 2
            self::validateType($set);
232
233 1
            $bit |= $set->bit;
234 1
        }
235
236 1
        $clone = new static();
237 1
        $clone->bit = $this->bit ^ $bit;
238
239 1
        return $clone;
240
    }
241
242
    /**
243
     * Get choices for checkbox group.
244
     *
245
     * <code>
246
     * {
247
     *   value1: 'Readable value 1',
248
     *   value2: 'Readable value 2',
249
     * }
250
     * </code>
251
     *
252
     * @return array
253
     */
254 1
    final public static function choices()
255
    {
256 1
        $class = get_called_class();
257 1
        self::detectConstants($class);
258
259 1
        $choices = [];
260 1
        foreach (self::$keys[$class] as $value) {
261 1
            $choices[$value] = static::readable($value);
262 1
        }
263
264 1
        return $choices;
265
    }
266
267
    /**
268
     * @param mixed $value
269
     *
270
     * @return string
271
     */
272 2
    public static function readable($value)
273
    {
274 2
        self::validateValue($value);
275
276 2
        return array_search($value, self::$keys[get_called_class()]);
277
    }
278
279
    /**
280
     * @param mixed $value
281
     */
282 23
    private static function validateValue($value)
283
    {
284 23
        if (!static::isValid($value)) {
285 3
            throw OutOfEnumException::create($value, get_called_class());
286
        }
287 23
    }
288
289
    /**
290
     * @param object $object
291
     */
292 15
    private static function validateType($object)
293
    {
294 15
        $class = get_called_class();
295 15
        if ($class !== get_class($object)) {
296 7
            throw InvalidSetException::notInstanceOf($class, get_class($object));
297
        }
298 8
    }
299
300
    /**
301
     * @param string $class
302
     */
303 23
    private static function detectConstants($class)
304
    {
305 23
        if (!isset(self::$bits[$class])) {
306 1
            self::$keys[$class] = [];
307 1
            self::$bits[$class] = [];
308 1
            $constants = [];
309 1
            $reflection = new \ReflectionClass($class);
310
311 1 View Code Duplication
            if (PHP_VERSION_ID >= 70100) {
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...
312
                // Since PHP-7.1 visibility modifiers are allowed for class constants
313
                // for enumerations we are only interested in public once.
314
                foreach ($reflection->getReflectionConstants() as $refl_constant) {
315
                    if ($refl_constant->isPublic()) {
316
                        $constants[$refl_constant->getName()] = $refl_constant->getValue();
317
                    }
318
                }
319
            } else {
320
                // In PHP < 7.1 all class constants were public by definition
321 1
                foreach ($reflection->getConstants() as $constant => $constant_value) {
322 1
                    $constants[$constant] = $constant_value;
323 1
                }
324
            }
325
326 1
            $bit = 1;
327 1
            foreach ($constants as $constant => $constant_value) {
328 1
                self::$keys[$class][$constant] = $constant_value;
329 1
                self::$bits[$class][$bit] = $constant_value;
330 1
                $bit += $bit;
331 1
            }
332 1
        }
333 23
    }
334
335
    /**
336
     * @param mixed $value
337
     *
338
     * @return int
339
     */
340 23
    private function bit($value)
341
    {
342 23
        return array_search($value, self::bits());
343
    }
344
345
    /**
346
     * @return array
347
     */
348 23
    private static function bits()
349
    {
350 23
        $class = get_called_class();
351 23
        self::detectConstants($class);
352
353 23
        return self::$bits[$class];
354
    }
355
}
356