Completed
Push — master ( 361c1c...8b1848 )
by Jesse
02:31
created

IdentityMap::addTo()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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