Completed
Pull Request — master (#49)
by Maxime
01:58
created

FlaggedEnum::withoutFlags()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 8
ccs 0
cts 0
cp 0
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 4
nc 2
nop 1
crap 6
1
<?php
2
3
/*
4
 * This file is part of the "elao/enum" package.
5
 *
6
 * Copyright (C) Elao
7
 *
8
 * @author Elao <[email protected]>
9
 */
10
11
namespace Elao\Enum;
12
13
use Elao\Enum\Exception\InvalidValueException;
14
use Elao\Enum\Exception\LogicException;
15
16
abstract class FlaggedEnum extends ReadableEnum
17
{
18
    use AutoDiscoveredReadablesTrait;
19
20
    const NONE = 0;
21
22
    /** @var array */
23
    private static $masks = [];
24
25
    /** @var int[] */
26
    protected $flags;
27
28
    /**
29 30
     * {@inheritdoc}
30
     */
31 30
    public static function accepts($value): bool
32 1
    {
33
        if (!is_int($value)) {
34
            return false;
35 29
        }
36 3
37
        if ($value === self::NONE) {
38
            return true;
39 26
        }
40
41
        return $value === ($value & self::getBitmask());
42
    }
43
44
    /**
45
     * {@inheritdoc}
46
     */
47 8
    public static function values(): array
48
    {
49 8
        return array_values(array_filter(parent::values(), function (int $v): bool {
50
            return 0 === ($v & $v - 1);
51
        }));
52 8
    }
53 1
54
    /**
55
     * {@inheritdoc}
56 7
     *
57
     * @param string $separator A delimiter used between each bit flag's readable string
58 7
     */
59 5
    public static function readableFor($value, string $separator = '; '): string
60
    {
61
        if (!static::accepts($value)) {
62 2
            throw new InvalidValueException($value, static::class);
63
        }
64 2
        if ($value === self::NONE) {
65 2
            return static::readableForNone();
66 2
        }
67
68
        $humanRepresentations = static::readables();
69
70 2
        if (isset($humanRepresentations[$value])) {
71
            return $humanRepresentations[$value];
72
        }
73
74
        $parts = [];
75
76
        foreach ($humanRepresentations as $flag => $readableValue) {
77
            if ($flag === ($flag & $value)) {
78 1
                $parts[] = $readableValue;
79
            }
80 1
        }
81
82
        return implode($separator, $parts);
83
    }
84
85
    /**
86
     * Gets the human representation for the none value.
87
     *
88
     * @return string
89
     */
90 26
    protected static function readableForNone(): string
91
    {
92 26
        return 'None';
93
    }
94 26
95 1
    /**
96 1
     * Gets an integer value of the possible flags for enumeration.
97 1
     *
98 1
     * @throws LogicException If the possibles values are not valid bit flags
99 1
     *
100
     * @return int
101 1
     */
102
    private static function getBitmask(): int
103
    {
104 1
        $enumType = static::class;
105
106
        if (!isset(self::$masks[$enumType])) {
107
            $mask = 0;
108
            foreach (static::values() as $flag) {
109 25
                if ($flag < 1 || ($flag > 1 && ($flag % 2) !== 0)) {
110
                    throw new LogicException(sprintf(
111
                        'Possible value %s of the enumeration "%s" is not a bit flag.',
112
                        json_encode($flag),
113
                        static::class
114
                    ));
115
                }
116
                $mask |= $flag;
117 7
            }
118
            self::$masks[$enumType] = $mask;
119 7
        }
120
121
        return self::$masks[$enumType];
122
    }
123
124
    /**
125
     * {@inheritdoc}
126
     *
127 13
     * @param string $separator A delimiter used between each bit flag's readable string
128
     */
129 13
    public function getReadable(string $separator = '; '): string
130 7
    {
131 7
        return static::readableFor($this->getValue(), $separator);
132 7
    }
133 7
134
    /**
135
     * Gets an array of bit flags of the value.
136
     *
137
     * @return array
138 13
     */
139
    public function getFlags(): array
140
    {
141
        if ($this->flags === null) {
142
            $this->flags = [];
143
            foreach (static::values() as $flag) {
144
                if ($this->hasFlag($flag)) {
145
                    $this->flags[] = $flag;
146
                }
147
            }
148 9
        }
149
150 9
        return $this->flags;
151 9
    }
152
153
    /**
154
     * Determines whether the specified flag is set in a numeric value.
155
     *
156
     * @param int $bitFlag The bit flag or bit flags
157
     *
158
     * @return bool True if the bit flag or bit flags are also set in the current instance; otherwise, false
159
     */
160
    public function hasFlag(int $bitFlag): bool
161
    {
162
        if ($bitFlag >= 1) {
163
            return $bitFlag === ($bitFlag & $this->value);
164
        }
165
166 2
        return false;
167
    }
168 2
169 1
    /**
170
     * Computes a new value with given flags, and returns the corresponding instance.
171
     *
172 1
     * @param int $flags The bit flag or bit flags
173
     *
174
     * @throws InvalidValueException When $flags is not acceptable for this enumeration type
175
     *
176
     * @return static The enum instance for computed value
177
     */
178
    public function withFlags(int $flags): self
0 ignored issues
show
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...
179
    {
180
        if (!static::accepts($flags)) {
181
            throw new InvalidValueException($flags, static::class);
182
        }
183
184 4
        return static::get($this->value | $flags);
185
    }
186 4
187 1
    /**
188
     * Computes a new value without given flags, and returns the corresponding instance.
189
     *
190 3
     * @param int $flags The bit flag or bit flags
191
     *
192
     * @throws InvalidValueException When $flags is not acceptable for this enumeration type
193
     *
194
     * @return static The enum instance for computed value
195
     */
196
    public function withoutFlags(int $flags): self
0 ignored issues
show
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...
197
    {
198
        if (!static::accepts($flags)) {
199
            throw new InvalidValueException($flags, static::class);
200
        }
201
202
        return static::get($this->value & ~$flags);
203
    }
204
}
205