Completed
Push — master ( 439e92...54d259 )
by Peter
08:34
created

Set::equal()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 9.4285
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 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 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
     * Check if this Set is the same as other.
102
     *
103
     * @param Set $set
104
     *
105
     * @return bool
106
     */
107 23
    final public function equal(Set $set)
108
    {
109 23
        self::validateType($set);
110
111
        return $this->bit === $this->bit;
112
    }
113
114
    /**
115
     * Check if this Set is a subset of other.
116
     *
117
     * @param Set $set
118
     *
119 3
     * @return bool
120
     */
121 3
    final public function subset(Set $set)
122
    {
123 2
        self::validateType($set);
124
125
        return ($this->bit & $set->bit) === $this->bit;
126
    }
127
128
    /**
129
     * Check if this Set is a superset of other.
130
     *
131
     * @param Set $set
132
     *
133 2
     * @return bool
134
     */
135 2
    final public function superset(Set $set)
136
    {
137 1
        self::validateType($set);
138
139
        return ($this->bit | $set->bit) === $this->bit;
140
    }
141
142
    /**
143
     * Produce a new set with enum from both this and other (this | other).
144
     *
145
     * @param Set ...$set Other Set(s) of the same enum to produce the union
146
     *
147 2
     * @return Set
148
     */
149 2 View Code Duplication
    final public function union(Set $set)
150
    {
151 1
        $bit = $this->bit;
152
        foreach (func_get_args() as $set) {
153
            self::validateType($set);
154
155
            $bit |= $set->bit;
156
        }
157
158
        $clone = new static();
159
        $clone->bit = $bit;
160
161 2
        return $clone;
162
    }
163 2
164 2
    /**
165 2
     * Produce a new set with enum common to both this and other (this & other).
166
     *
167 1
     * @param Set ...$set Other Set(s) of the same enumeration to produce the union
168 1
     *
169
     * @return Set
170 1
     */
171 1 View Code Duplication
    final public function intersect(Set $set)
172
    {
173 1
        $bit = $this->bit;
174
        foreach (func_get_args() as $set) {
175
            self::validateType($set);
176
177
            $bit &= $set->bit;
178
        }
179
180
        $clone = new static();
181
        $clone->bit = $bit;
182
183 2
        return $clone;
184
    }
185 2
186 2
    /**
187 2
     * Produce a new set with enum in this but not in other (this - other).
188
     *
189 1
     * @param Set ...$set Other Set(s) of the same enumeration to produce the union
190 1
     *
191
     * @return Set
192 1
     */
193 1 View Code Duplication
    final public function diff(Set $set)
194
    {
195 1
        $bit = 0;
196
        foreach (func_get_args() as $set) {
197
            self::validateType($set);
198
199
            $bit |= $set->bit;
200
        }
201
202
        $clone = new static();
203
        $clone->bit = $this->bit & ~$bit;
204
205 2
        return $clone;
206
    }
207 2
208 2
    /**
209 2
     * Produce a new set with enum in either this and other but not in both (this ^ (other | other)).
210
     *
211 1
     * @param Set ...$set Other Set(s) of the same enumeration to produce the union
212 1
     *
213
     * @return Set
214 1
     */
215 1 View Code Duplication
    final public function symDiff(Set $set)
216
    {
217 1
        $bit = 0;
218
        foreach (func_get_args() as $set) {
219
            self::validateType($set);
220
221
            $bit |= $set->bit;
222
        }
223
224
        $clone = new static();
225
        $clone->bit = $this->bit ^ $bit;
226
227 2
        return $clone;
228
    }
229 2
230 2
    /**
231 2
     * Get choices for checkbox group.
232
     *
233 1
     * <code>
234 1
     * {
235
     *   value1: 'Readable value 1',
236 1
     *   value2: 'Readable value 2',
237 1
     * }
238
     * </code>
239 1
     *
240
     * @return array
241
     */
242
    final public static function choices()
243
    {
244
        $class = get_called_class();
245
        self::detectConstants($class);
246
247
        $choices = [];
248
        foreach (self::$keys[$class] as $value) {
249
            $choices[$value] = static::readable($value);
250
        }
251
252
        return $choices;
253
    }
254 1
255
    /**
256 1
     * @param mixed $value
257 1
     *
258
     * @return string
259 1
     */
260 1
    public static function readable($value)
261 1
    {
262 1
        self::validateValue($value);
263
264 1
        return array_search($value, self::$keys[get_called_class()]);
265
    }
266
267
    /**
268
     * @param mixed $value
269
     */
270
    private static function validateValue($value)
271
    {
272 2
        if (!in_array($value, self::bits(), true)) {
273
            throw OutOfEnumException::invalidValue($value, get_called_class());
274 2
        }
275
    }
276 2
277
    /**
278
     * @param object $object
279
     */
280
    private static function validateType($object)
281
    {
282 23
        $class = get_called_class();
283
        if ($class !== get_class($object)) {
284 23
            throw InvalidSetException::notInstanceOf($class, get_class($object));
285 3
        }
286
    }
287 23
288
    /**
289
     * @param string $class
290
     */
291
    private static function detectConstants($class)
292 15
    {
293
        if (!isset(self::$bits[$class])) {
294 15
            self::$keys[$class] = [];
295 15
            self::$bits[$class] = [];
296 7
297
            $bit = 1;
298 8
            foreach (ConstantDetector::detect($class) as $constant => $constant_value) {
299
                self::$keys[$class][$constant] = $constant_value;
300
                self::$bits[$class][$bit] = $constant_value;
301
                $bit += $bit;
302
            }
303 23
        }
304
    }
305 23
306 1
    /**
307 1
     * @param mixed $value
308
     *
309 1
     * @return int
310 1
     */
311 1
    private function bit($value)
312 1
    {
313 1
        return array_search($value, self::bits());
314 1
    }
315 1
316 23
    /**
317
     * @return array
318
     */
319
    private static function bits()
320
    {
321
        $class = get_called_class();
322
        self::detectConstants($class);
323 23
324
        return self::$bits[$class];
325 23
    }
326
}
327