Completed
Push — master ( f9372b...e0fd03 )
by Jesse
02:11
created

IdentityMap::idOf()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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