Completed
Push — master ( e0fd03...97dbe0 )
by Jesse
07:35
created

IdentityMap::classes()   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 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Stratadox\IdentityMap;
5
6
use function array_keys;
7
use function array_merge as these;
8
use function array_values as allThese;
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 idOf(object $object): string
111
    {
112
        if (!isset($this->entityIdFor[theInstanceIdOf($object)])) {
113
            throw IdentityNotFound::forThe($object);
114
        }
115
        return $this->entityIdFor[theInstanceIdOf($object)];
116
    }
117
118
    public function objects(): array
119
    {
120
        if (weDidNotYetList($this->objects)) {
121
            $objects = [];
122
            foreach ($this->objectWith as $class => $objectsById) {
123
                $objects[] = allThese($objectsById);
124
            }
125
            $this->objects = these(...$objects);
126
        }
127
        return $this->objects;
128
    }
129
130
    /** @throws NoSuchObject */
131
    private function mustHave(string $class, string $id): void
132
    {
133
        if ($this->has($class, $id)) {
134
            return;
135
        }
136
        throw IdentityNotFound::requesting($class, $id);
137
    }
138
139
    /** @throws AlreadyThere */
140
    private function mayNotAlreadyHave(string $class, string $id): void
141
    {
142
        if ($this->has($class, $id)) {
143
            throw DuplicationDetected::in($class, $id);
144
        }
145
    }
146
147
    private static function addTo(
148
        array $objectsBy,
149
        string $withId,
150
        object $object
151
    ): array {
152
        $objectsBy[theClassOfThe($object)][$withId] = $object;
153
        return $objectsBy;
154
    }
155
}
156