Completed
Push — v3.0 ( 630977...7777a9 )
by Thierry
01:25
created

Provider::hydrate()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4.128

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 17
ccs 8
cts 10
cp 0.8
rs 9.9
c 0
b 0
f 0
cc 4
nc 4
nop 2
crap 4.128
1
<?php
2
declare(strict_types=1);
3
4
namespace Byscripts\StaticEntity;
5
6
class Provider implements ProviderInterface
7
{
8
    static private $instances = [];
9
10
    /**
11
     * @param string $className
12
     * @param mixed  $id
13
     *
14
     * @return StaticEntityInterface
15
     */
16 8
    static public function get(string $className, $id): StaticEntityInterface
17
    {
18 8
        if (isset(self::$instances[$className][$id])) {
19 5
            return self::$instances[$className][$id];
20
        }
21
22 3
        self::checkClass($className);
23
24 3
        $dataSet = self::getDataSet($className);
25
26 3
        if (!isset($dataSet[$id])) {
27 2
            throw new \InvalidArgumentException(
28 2
                sprintf('Unable to find data for class "%s" with ID "%s"', $className, $id)
29
            );
30
        }
31
32 1
        self::build($className, $id, $dataSet[$id]);
33
34 1
        return self::$instances[$className][$id];
35
    }
36
37
    /**
38
     * @param string $className
39
     *
40
     * @return array
41
     */
42 7
    static public function getAll(string $className): array
43
    {
44 7
        self::checkClass($className);
45
46 6
        $dataSet = self::getDataSet($className);
47
48 6
        if (isset(self::$instances[$className]) && count($dataSet) === count(self::$instances[$className])) {
49 3
            return self::$instances[$className];
50
        }
51
52 3
        $ids = array_keys($dataSet);
53
54 3
        foreach ($ids as $id) {
55 3
            self::build($className, $id, $dataSet[$id]);
56
        }
57
58 1
        return self::$instances[$className];
59
    }
60
61
    /**
62
     * @param string $className
63
     * @param mixed  $id
64
     *
65
     * @return bool
66
     */
67 6
    static public function hasId(string $className, $id): bool
68
    {
69 6
        self::checkClass($className);
70
71 6
        $dataSet = self::getDataSet($className);
72
73 6
        return isset($dataSet[$id]);
74
    }
75
76
    /**
77
     * @param string $className
78
     * @param mixed  $classOrId
79
     *
80
     * @return mixed
81
     */
82 6
    static public function toId(string $className, $classOrId)
83
    {
84 6
        self::checkClass($className);
85
86 6
        if ($classOrId instanceof StaticEntityInterface) {
87 2
            return $classOrId->getId();
88 6
        } elseif (is_object($classOrId)) {
89 2
            throw new \InvalidArgumentException(
90 2
                sprintf('Class "%s" does not implement "%s"', get_class(), StaticEntityInterface::class)
91
            );
92
        }
93
94 4
        if (self::hasId($className, $classOrId)) {
95 2
            return $classOrId;
96
        }
97
98 2
        throw new \InvalidArgumentException(
99 2
            sprintf('Unable to find ID "%s" in dataSet of class "%s"', $classOrId, $className)
100
        );
101
    }
102
103
    /**
104
     * @param string $className
105
     * @param string $key
106
     *
107
     * @return array
108
     */
109 2
    static public function getAssociative(string $className, string $key = 'name'): array
110
    {
111 2
        self::checkClass($className);
112
113 2
        $dataSet = self::getDataSet($className);
114
115 2
        return array_map(
116
            function ($data) use ($key) {
117 2
                return $data[$key];
118 2
            },
119 2
            $dataSet
120
        );
121
    }
122
123
    /**
124
     * @param string $className
125
     *
126
     * @return array
127
     */
128 2
    static public function getIds(string $className): array
129
    {
130 2
        self::checkClass($className);
131
132 2
        $dataSet = self::getDataSet($className);
133
134 2
        return array_keys($dataSet);
135
    }
136
137
    /**
138
     * @param string $className
139
     */
140 22
    static private function checkClass(string $className): void
141
    {
142 22
        $interfaceName = StaticEntityInterface::class;
143
144 22
        if (!is_subclass_of($className, $interfaceName)) {
145 1
            throw new \InvalidArgumentException("${className} must implements ${interfaceName}");
146
        }
147 21
    }
148
149
    /**
150
     * @param string $className
151
     *
152
     * @return array
153
     */
154
    static private function getDataSet(string $className): array
155
    {
156 19
        return call_user_func([$className, 'getDataSet']);
157
    }
158
159
    /**
160
     * @param string $className
161
     * @param mixed  $id
162
     * @param array  $data
163
     */
164
    static private function build(string $className, $id, array $data): void
165
    {
166 4
        if (isset(self::$instances[$className][$id])) {
167 1
            return;
168
        }
169
170 4
        $instance = new $className();
171
172 4
        $data['id'] = $id;
173 4
        self::hydrate($instance, $data);
174
175 2
        self::$instances[$className][$id] = $instance;
176 2
    }
177
178
    /**
179
     * @param StaticEntityInterface $instance
180
     * @param array                 $data
181
     */
182
    static private function hydrate(StaticEntityInterface $instance, array $data): void
183
    {
184
        try {
185 4
            $reflectionClass = new \ReflectionClass($instance);
186
        } catch (\Throwable $e) {
187
            return;
188
        }
189
190 4
        foreach ($data as $key => $value) {
191 4
            if (!$reflectionClass->hasProperty($key)) {
192 2
                throw new \InvalidArgumentException(
193 2
                    sprintf('Class "%s" has no property "%s"', $reflectionClass->name, $key)
194
                );
195
            }
196 2
            $property = $reflectionClass->getProperty($key);
197 2
            $property->setAccessible(true);
198 2
            $property->setValue($instance, $value);
199
        }
200 2
    }
201
}
202