Skip to content

Commit 2c847ff

Browse files
committed
Hydrate scenarios with arrays/iterators
1 parent 0c8c1cc commit 2c847ff

File tree

1 file changed

+105
-27
lines changed

1 file changed

+105
-27
lines changed

src/ServiceContainer.php

Lines changed: 105 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use ReflectionMethod;
1818
use Webdevcave\DirectoryCrawler\Crawler;
1919
use Webdevcave\SimpleCache\MemoryCache;
20+
use Webdevcave\Yadic\Annotations\ArrayOf;
2021
use Webdevcave\Yadic\Annotations\Inject;
2122
use Webdevcave\Yadic\Annotations\Provides;
2223
use Webdevcave\Yadic\Annotations\Singleton;
@@ -113,42 +114,22 @@ public function has(string $id): bool
113114
* @param T $classOrObject
114115
* @param array $data
115116
*
116-
* @return T
117+
* @return T|T[]
117118
* @throws ContainerException
118119
*
119120
*/
120121
public function hydrate(string|object $classOrObject, array $data = []): mixed
121122
{
122123
try {
123-
$instance = $classOrObject;
124-
125-
if (is_string($instance)) {
126-
$arguments = [];
127-
$reflection = new ReflectionClass($instance);
128-
129-
if ($reflection->hasMethod('__construct')) {
130-
$reflectionMethod = $reflection->getMethod('__construct');
131-
$argumentsMap = [];
132-
133-
foreach ($reflectionMethod->getParameters() as $parameter) {
134-
$reflectionType = $parameter->getType();
135-
136-
$argumentsMap[$parameter->getName()] = [
137-
'type' => $reflectionType->getName(),
138-
'isBuiltin' => $reflectionType->isBuiltin(),
139-
];
140-
}
141-
142-
foreach (array_intersect_key($data, $argumentsMap) as $key => $value) {
143-
$arguments[$key] = !$argumentsMap[$key]['isBuiltin'] ?
144-
$this->hydrate($argumentsMap[$key]['type'], $value ?? []) : $value;
145-
}
146-
}
124+
if (array_is_list($data)) {
125+
return $this->hydrateArray($classOrObject, $data);
126+
}
147127

148-
$instance = $this->get($instance, $arguments);
128+
if (is_string($classOrObject)) {
129+
return $this->hydrateByClassName($classOrObject, $data);
149130
}
150131

151-
return $instance;
132+
return $this->hydrateExistingInstance($classOrObject, $data);
152133
} catch (Exception $e) {
153134
throw new ContainerException('Could not hydrate object', $e->getCode(), $e);
154135
}
@@ -267,4 +248,101 @@ public function addAlias(string $alias, string $concrete): void
267248
{
268249
$this->aliases[$alias] = $concrete;
269250
}
251+
252+
/**
253+
* @template T
254+
*
255+
* @param T $classOrObject
256+
* @param array $data
257+
*
258+
* @return T[]
259+
*/
260+
private function hydrateArray(string|object $classOrObject, array $data): mixed
261+
{
262+
if (is_object($classOrObject)) {
263+
$classOrObject = get_class($classOrObject);
264+
}
265+
266+
$objects = [];
267+
268+
foreach ($data as $item) {
269+
$objects[] = $this->hydrateByClassName($classOrObject, $item);
270+
}
271+
272+
return $objects;
273+
}
274+
275+
/**
276+
* @param string $className
277+
* @param array $data
278+
*
279+
* @throws ContainerException
280+
* @throws ContainerExceptionInterface
281+
* @throws NotFoundExceptionInterface
282+
* @throws ReflectionException
283+
*
284+
* @return mixed
285+
*/
286+
private function hydrateByClassName(string $className, array $data): mixed
287+
{
288+
$arguments = [];
289+
$reflection = new ReflectionClass($className);
290+
291+
if ($reflection->hasMethod('__construct')) {
292+
$reflectionMethod = $reflection->getMethod('__construct');
293+
$argumentsMap = [];
294+
295+
foreach ($reflectionMethod->getParameters() as $parameter) {
296+
$reflectionType = $parameter->getType();
297+
$type = $reflectionType->getName();
298+
$arrayType = null;
299+
300+
if (
301+
($type === 'array' || $type === 'iterable')
302+
&& !empty($arrayOf = $parameter->getAttributes(ArrayOf::class))
303+
) {
304+
$arrayType = $arrayOf[0]->newInstance()->target;
305+
}
306+
307+
$argumentsMap[$parameter->getName()] = [
308+
'type' => $type,
309+
'arrayType' => $arrayType,
310+
'isBuiltin' => $reflectionType->isBuiltin(),
311+
];
312+
}
313+
314+
foreach (array_intersect_key($data, $argumentsMap) as $key => $value) {
315+
$map = $argumentsMap[$key];
316+
317+
if (!is_null($map['arrayType']) && is_iterable($value)) {
318+
$arguments[$key] = [];
319+
320+
foreach ($value as $item) {
321+
$arguments[$key][] = $this->hydrateByClassName($map['arrayType'], $item);
322+
}
323+
324+
continue;
325+
}
326+
327+
$arguments[$key] = !$map['isBuiltin'] ?
328+
$this->hydrateByClassName($map['type'], $value ?? []) : $value;
329+
}
330+
}
331+
332+
return $this->get($className, $arguments);
333+
}
334+
335+
/**
336+
* @param object $instance
337+
* @param array $data
338+
*
339+
* @throws Exception
340+
*
341+
* @return mixed
342+
*/
343+
private function hydrateExistingInstance(object $instance, array $data): mixed
344+
{
345+
//TODO implementation
346+
throw new Exception('Not implemented');
347+
}
270348
}

0 commit comments

Comments
 (0)