Passed
Push — master ( c2231d...b4845c )
by Jesse
02:15
created

IdentityMap::hasThe()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
rs 10
cc 1
eloc 2
nc 1
nop 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Stratadox\IdentityMap;
5
6
use function assert as makeSureThat;
7
use function get_class as theClassOfThe;
8
use function is_object as itIsAn;
9
use function spl_object_id as theInstanceIdOf;
10
11
/**
12
 * Contains objects by class and id.
13
 *
14
 * @author Stratadox
15
 */
16
final class IdentityMap implements MapsObjectsByIdentity
17
{
18
    private $objectsBy;
19
    private $entityIdFor;
20
21
    private function __construct(array $objectsByClassAndId, array $idsByObject)
22
    {
23
        $this->objectsBy = $objectsByClassAndId;
24
        $this->entityIdFor = $idsByObject;
25
    }
26
27
    /**
28
     * Produces a new identity map that contains the objects.
29
     *
30
     * @param object[] $theseObjects The objects to add, as [id => object]
31
     * @return MapsObjectsByIdentity The map of objects.
32
     */
33
    public static function with(array $theseObjects): MapsObjectsByIdentity
34
    {
35
        $objects = [];
36
        $entityIds = [];
37
        foreach ($theseObjects as $id => $object) {
38
            $objects = IdentityMap::addTo($objects, (string) $id, $object);
39
            $entityIds[theInstanceIdOf($object)] = (string) $id;
40
        }
41
        return new self($objects, $entityIds);
42
    }
43
44
    /**
45
     * Produces an empty identity map.
46
     *
47
     * @return MapsObjectsByIdentity The empty map of objects.
48
     */
49
    public static function startEmpty(): MapsObjectsByIdentity
50
    {
51
        return new self([], []);
52
    }
53
54
    /** @inheritdoc */
55
    public function has(string $class, string $id): bool
56
    {
57
        return isset($this->objectsBy[$class][$id]);
58
    }
59
60
    /** @inheritdoc */
61
    public function hasThe(object $object): bool
62
    {
63
        return isset($this->entityIdFor[theInstanceIdOf($object)]);
64
    }
65
66
    /** @inheritdoc */
67
    public function get(string $class, string $id): object
68
    {
69
        $this->mustHave($class, $id);
70
        return $this->objectsBy[$class][$id];
71
    }
72
73
    /** @inheritdoc */
74
    public function add(string $id, object $object): MapsObjectsByIdentity
75
    {
76
        $class = theClassOfThe($object);
77
        $this->mayNotAlreadyHave($class, $id);
78
79
        $new = clone $this;
80
        $new->objectsBy[$class][$id] = $object;
81
        $new->entityIdFor[theInstanceIdOf($object)] = $id;
82
        return $new;
83
    }
84
85
    /** @inheritdoc */
86
    public function remove(string $class, string $id): MapsObjectsByIdentity
87
    {
88
        $this->mustHave($class, $id);
89
        $entityIdFor = $this->entityIdFor;
90
        $objectsBy = $this->objectsBy;
91
        unset(
92
            $entityIdFor[theInstanceIdOf($objectsBy[$class][$id])],
93
            $objectsBy[$class][$id]
94
        );
95
        return new IdentityMap($objectsBy, $entityIdFor);
96
    }
97
98
    /** @inheritdoc */
99
    public function removeThe(object $object): MapsObjectsByIdentity
100
    {
101
        return $this->remove(theClassOfThe($object), $this->idOf($object));
102
    }
103
104
    /** @inheritdoc */
105
    public function removeAllObjectsOfThe(string $class): MapsObjectsByIdentity
106
    {
107
        $objectsBy = $this->objectsBy;
108
        if (!isset($objectsBy[$class])) {
109
            return $this;
110
        }
111
        $entityIdFor = $this->entityIdFor;
112
        foreach ($objectsBy[$class] as $object) {
113
            makeSureThat(itIsAn($object));
114
            unset($entityIdFor[theInstanceIdOf($object)]);
115
        }
116
        unset($objectsBy[$class]);
117
        return new IdentityMap($objectsBy, $entityIdFor);
118
    }
119
120
    /** @inheritdoc */
121
    public function idOf(object $object): string
122
    {
123
        if (!isset($this->entityIdFor[theInstanceIdOf($object)])) {
124
            throw IdentityNotFound::forThe($object);
125
        }
126
        return $this->entityIdFor[theInstanceIdOf($object)];
127
    }
128
129
    /**
130
     * Asserts that the object of the class with this id is present in the map.
131
     *
132
     * @param string $class The class of the object to check for.
133
     * @param string $id    The identity of the object, unique per class.
134
     * @throws NoSuchObject When there is no object with this id in the map.
135
     */
136
    private function mustHave(string $class, string $id): void
137
    {
138
        if ($this->has($class, $id)) {
139
            return;
140
        }
141
        throw IdentityNotFound::requesting($class, $id);
142
    }
143
144
    /**
145
     * Asserts that the object of the class with this id is not already there.
146
     *
147
     * @param string $class The class of the object to check for.
148
     * @param string $id    The identity of the object, unique per class.
149
     * @throws AlreadyThere When there is already an object with this id.
150
     */
151
    private function mayNotAlreadyHave(string $class, string $id): void
152
    {
153
        if ($this->has($class, $id)) {
154
            throw DuplicationDetected::in($class, $id);
155
        }
156
    }
157
158
    /**
159
     * Adds the object to the map, returning the new map.
160
     *
161
     * @param array  $objectsBy The original map.
162
     * @param string $withId    The id of the object to add.
163
     * @param object $object    The object instance to add.
164
     * @return array            A new map that includes the new object.
165
     */
166
    private static function addTo(
167
        array $objectsBy,
168
        string $withId,
169
        object $object
170
    ): array {
171
        $objectsBy[theClassOfThe($object)][$withId] = $object;
172
        return $objectsBy;
173
    }
174
}
175