Completed
Push — master ( e61af3...334344 )
by Peter
05:18
created

Set::contains()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
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 24
    final public function __construct(array $values = [])
37
    {
38 24
        foreach ($values as $value) {
39 24
            $this->attach($value);
40
        }
41 24
    }
42
43
    /**
44
     * @return mixed[]
45
     */
46 5
    final public function values()
47
    {
48 5
        if (!$this->bit) {
49 1
            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
            }
57
        }
58
59 5
        return $values;
60
    }
61
62
    /**
63
     * Attach the given value.
64
     *
65
     * @param mixed $value
66
     */
67 24
    final public function attach($value)
68
    {
69 24
        self::validateValue($value);
70
71 24
        $this->bit |= $this->bit($value);
72 24
    }
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 24
    final public static function isValid($value)
108
    {
109 24
        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)
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
        }
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)
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
        }
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)
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
        }
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)
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
        }
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 2
    final public static function choices()
255
    {
256 2
        $class = get_called_class();
257 2
        self::detectConstants($class);
258
259 2
        $choices = [];
260 2
        foreach (self::$keys[$class] as $value) {
261 2
            $choices[$value] = static::readable($value);
262
        }
263
264 2
        return $choices;
265
    }
266
267
    /**
268
     * @param mixed $value
269
     *
270
     * @return string
271
     */
272 3
    public static function readable($value)
273
    {
274 3
        self::validateValue($value);
275
276 3
        return array_search($value, self::$keys[get_called_class()]);
277
    }
278
279
    /**
280
     * @param mixed $value
281
     */
282 24
    private static function validateValue($value)
283
    {
284 24
        if (!static::isValid($value)) {
285 3
            throw OutOfEnumException::create($value, get_called_class());
286
        }
287 24
    }
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 24
    private static function detectConstants($class)
304
    {
305 24
        if (!isset(self::$bits[$class])) {
306 2
            self::$keys[$class] = [];
307 2
            self::$bits[$class] = [];
308
309 2
            $bit = 1;
310 2
            foreach (ConstantDetector::detect($class) as $constant => $constant_value) {
311 2
                self::$keys[$class][$constant] = $constant_value;
312 2
                self::$bits[$class][$bit] = $constant_value;
313 2
                $bit += $bit;
314
            }
315
        }
316 24
    }
317
318
    /**
319
     * @param mixed $value
320
     *
321
     * @return int
322
     */
323 24
    private function bit($value)
324
    {
325 24
        return array_search($value, self::bits());
326
    }
327
328
    /**
329
     * @return array
330
     */
331 24
    private static function bits()
332
    {
333 24
        $class = get_called_class();
334 24
        self::detectConstants($class);
335
336 24
        return self::$bits[$class];
337
    }
338
}
339