Completed
Push — master ( 84b750...43e2b4 )
by Alex
21s queued 11s
created

BitMask   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 112
Duplicated Lines 0 %

Importance

Changes 16
Bugs 1 Features 1
Metric Value
eloc 42
c 16
b 1
f 1
dl 0
loc 112
rs 10
wmc 21

9 Methods

Rating   Name   Duplication   Size   Complexity  
A isFlag() 0 3 2
A isValid() 0 5 2
A getName() 0 4 1
A toArray() 0 14 3
A setFlag() 0 8 4
A __call() 0 10 3
A __toString() 0 11 3
A getKey() 0 10 1
A isComponentOfFlag() 0 3 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cruxinator\BitMask;
6
7
use BadMethodCallException;
8
use MyCLabs\Enum\Enum;
9
use UnexpectedValueException;
10
11
abstract class BitMask extends Enum
12
{
13
    // Because 0 is the identity element when ORing a bitmask, we have to special-case it when ANDing bitmasks
14
    // (akin to ordinary division by 0, the addition identity element on integers/rationals/reals)
15
16
    protected function isFlag(int $flag): bool
17
    {
18
        return 0 === $flag ? 0 === $this->value : (($this->value & $flag) == $flag);
19
    }
20
21
    protected function isComponentOfFlag(int $flag): bool
22
    {
23
        return 0 === $flag ? 0 === $this->value : (($this->value & $flag) == $this->value);
24
    }
25
26
    protected function setFlag(int $flag, bool $value)
27
    {
28
        if ($value) {
29
            $this->value = 0 === $flag ? 0 : $this->value | $flag;
30
        } else {
31
            $this->value = 0 === $flag  ? $this->value : $this->value & ~$flag;
32
        }
33
        return $this;
34
    }
35
36
    /**
37
     * @param $name
38
     * @param $arguments
39
     * @throws \ReflectionException
40
     * @return bool|self
41
     */
42
    public function __call($name, $arguments)
43
    {
44
        $array     = static::toArray();
45
        $regexBase = '/(isComponentOf|is|set)(%s)/m';
46
        $regexFull = sprintf($regexBase, implode('$|', array_keys($array)));
47
        preg_match($regexFull, $name, $match);
48
        if (count($match)>0 && $match[0] === $name) {
49
            return $this->{$match[1] . 'Flag'}($array[$match[2]], $arguments[0] ?? true);
50
        }
51
        throw new BadMethodCallException(sprintf('Enum %s not found on %s', $name, get_class($this)));
52
    }
53
54
    /**
55
     * @param $value
56
     * @throws \ReflectionException
57
     * @return bool
58
     */
59
    public static function isValid($value): bool
60
    {
61
        $min = min(static::toArray());
62
        $max = max(static::toArray()) * 2 - 1;
63
        return $value >= $min && $value <= $max;
64
    }
65
66
    /**
67
     * @throws \ReflectionException
68
     * @return array
69
     */
70
    public static function toArray(): array
71
    {
72
        $firstTime = !isset(static::$cache[static::class]);
73
        $array     = array_filter(parent::toArray(), function ($value): bool {
74
            return is_scalar($value);
75
        });
76
        $firstTime && array_walk($array, function ($item): void {
77
            if (!is_integer($item)) {
78
                throw new UnexpectedValueException(
79
                    sprintf('All defined Const on Enum %s should be integers', static::class)
80
                );
81
            }
82
        });
83
        return $array;
84
    }
85
86
    /**
87
     * @throws \ReflectionException
88
     * @return array|mixed
89
     */
90
    public function getKey()
91
    {
92
        $value = $this->value;
93
        $f     = array_filter(static::toArray(), function () use (&$value) {
94
            $isSet = $value & 1;
95
            $value = $value >> 1;
96
            return $isSet;
97
        });
98
99
        return array_keys($f);
100
    }
101
102
    /**
103
     * @throws \ReflectionException
104
     * @return string
105
     */
106
    public function __toString(): string
107
    {
108
        $name  = $this->getName();
109
        $array = static::toArray();
110
        $ret   = '';
111
        foreach ($array as $key => $value) {
112
            $ret .= "'" . $key . "' => " . ($this->{'is' . $key}() ? 'TRUE' : 'FALSE') . PHP_EOL;
113
        }
114
        return $name . '[' . PHP_EOL .
115
            $ret .
116
            ']' . PHP_EOL;
117
    }
118
119
    public function getName(): string
120
    {
121
        $path = explode('\\', __CLASS__);
122
        return array_pop($path);
123
    }
124
}
125