Issues (12)

src/Set/Converter/BitMaskConverter.php (1 issue)

Severity
1
<?php
2
namespace SpareParts\Enum\Set\Converter;
3
4
use SpareParts\Enum\Enum;
5
use SpareParts\Enum\Exception\EnumSetMustContainEnumsException;
6
use SpareParts\Enum\Set\ISet;
7
use SpareParts\Enum\Set\ImmutableSet;
8
9
class BitMaskConverter implements IEnumSetConverter
10
{
11
    /**
12
     * @var string
13
     */
14
    private $enumClass;
15
16
    /**
17
     * @var array
18
     */
19
    private $mapping;
20
21
    /**
22
     * @param Enum[] $enumValuesMap
23
     */
24 13
    public function __construct(iterable $enumValuesMap)
25
    {
26 13
        if (!count($enumValuesMap)) {
27 1
            throw new EnumSetMustContainEnumsException('You have to provide converter mapping.');
28
        }
29 12
        $this->mapping = $this->prepareInnerMapping($enumValuesMap);
30 9
    }
31
32
    /**
33
     * @param int $values A number representing a bit mask
34
     * @return ISet
35
     */
36 5
    public function convertToEnumSet($values) : ISet
37
    {
38 5
        $bitValue = (int) $values;
39
40 5
        $set = [];
41 5
        foreach ($this->mapping as $value => $bit) {
42 5
            if ($bitValue & $bit) {
43 5
                $set[] = call_user_func([$this->enumClass, 'instance'], $value);
44
            }
45
        }
46 5
        return new ImmutableSet($this->enumClass, $set);
47
    }
48
49 4
    public function convertFromEnumSet(ISet $enumSet): int
50
    {
51 4
        $bitValue = 0;
52
        /** @var Enum $value */
53 4
        foreach ($enumSet as $value) {
54 3
            $bitValue = $bitValue | $this->mapping[(string) $value];
55
        }
56 4
        return $bitValue;
57
    }
58
59 12
    private function prepareInnerMapping(iterable $enumValuesMap): array
60
    {
61
        // translate enum values to bit values mapping
62 12
        $innerMapping = [];
63 12
        $shift = 0;
64 12
        foreach ($enumValuesMap as $value) {
65 12
            $this->updateEnumClass($value);
66
67 11
            $innerMapping[(string)$value] = 1 << $shift;
68 11
            $shift++;
69
        }
70 9
        return $innerMapping;
71
    }
72
73 11
    private function updateEnumClass(Enum $value): void
74
    {
75
        // try to guess enum class
76 11
        if (is_null($this->enumClass)) {
0 ignored issues
show
The condition is_null($this->enumClass) is always false.
Loading history...
77 11
            $this->enumClass = get_class($value);
78
        }
79 11
        if (!is_object($value) || !($value instanceof $this->enumClass)) {
80 1
            throw new EnumSetMustContainEnumsException(sprintf("Expected `%s`, got `%s`", $this->enumClass, $this->printVar($value)));
81
        }
82 11
    }
83
84
    /**
85
     * @param mixed $value
86
     * @return string
87
     */
88 1
    private function printVar($value): string
89
    {
90 1
        return is_object($value) ? get_class($value) : var_export($value, true);
91
    }
92
}
93