Completed
Pull Request — master (#74)
by Benjamin
02:07
created

FlaggedEnum   A

Complexity

Total Complexity 30

Size/Duplication

Total Lines 188
Duplicated Lines 7.45 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 94.44%

Importance

Changes 0
Metric Value
wmc 30
lcom 1
cbo 3
dl 14
loc 188
rs 10
c 0
b 0
f 0
ccs 51
cts 54
cp 0.9444

10 Methods

Rating   Name   Duplication   Size   Complexity  
A accepts() 0 12 3
A readables() 14 14 3
B readableFor() 0 25 6
A readableForNone() 0 4 1
A getBitmask() 0 17 6
A getReadable() 0 4 1
A getFlags() 0 13 4
A hasFlag() 0 8 2
A withFlags() 0 8 2
A withoutFlags() 0 8 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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