Hydrator::loadFromDefaults()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 1
eloc 2
c 2
b 1
f 0
nc 1
nop 0
dl 0
loc 4
ccs 3
cts 3
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Maiorano\ObjectHydrator;
4
5
use Generator;
6
use Maiorano\ObjectHydrator\Attributes\HydrationStrategy;
7
use Maiorano\ObjectHydrator\Strategies\HydrationStrategyInterface;
8
use Maiorano\ObjectHydrator\Strategies\Reflection\MethodsStrategy;
9
use Maiorano\ObjectHydrator\Strategies\Reflection\PropertiesStrategy;
10
use ReflectionAttribute;
11
use ReflectionClass;
12
use ReflectionException;
13
use stdClass;
14
15
final class Hydrator implements HydratorInterface
16
{
17
    /**
18
     * @param string|object $object
19
     * @param array         $input
20
     *
21
     * @throws ReflectionException
22
     *
23
     * @return object
24
     */
25 6
    public function hydrate(string|object $object, array $input): object
26
    {
27 6
        $reflect = new ReflectionClass($object);
28 6
        if (is_string($object)) {
29 5
            $object = $reflect->newInstanceWithoutConstructor();
30
        }
31
32 6
        $strategies = iterator_to_array($this->generateStrategies($reflect));
33 6
        foreach ($input as $key => $value) {
34 6
            if ($strategy = $this->locateStrategy($strategies, $object, $key)) {
35 6
                $this->executeStrategy($strategy, $object, $key, $value);
36
            }
37
        }
38
39 6
        return $object;
40
    }
41
42
    /**
43
     * @param ReflectionClass $reflect
44
     *
45
     * @return Generator
46
     */
47 6
    private function generateStrategies(ReflectionClass $reflect): Generator
48
    {
49 6
        $attributes = $reflect->getAttributes(HydrationStrategy::class);
50 6
        yield from $attributes
51 5
            ? $this->loadFromAttributes($attributes)
52 4
            : $this->loadFromDefaults();
53 6
    }
54
55
    /**
56
     * @param ReflectionAttribute[] $attributes
57
     *
58
     * @return Generator
59
     */
60 5
    private function loadFromAttributes(array $attributes): Generator
61
    {
62 5
        foreach ($attributes as $attribute) {
63 5
            $key = $attribute->newInstance();
64 5
            $name = $key->getStrategy();
65 5
            yield new $name(...$key->getArgs());
66
        }
67 5
    }
68
69
    /**
70
     * @return Generator
71
     */
72 4
    private function loadFromDefaults(): Generator
73
    {
74 4
        yield new PropertiesStrategy();
75 4
        yield new MethodsStrategy();
76 4
    }
77
78
    /**
79
     * @param HydrationStrategyInterface[] $strategies
80
     * @param object                       $object
81
     * @param string                       $key
82
     *
83
     * @return HydrationStrategyInterface|null
84
     */
85 6
    private function locateStrategy(array $strategies, object $object, string $key): ?HydrationStrategyInterface
86
    {
87 6
        foreach ($strategies as $strategy) {
88 6
            $strategy->initialize($object);
89 6
            if ($strategy->hasMatchingKey($key)) {
90 6
                return $strategy;
91
            }
92
        }
93
94 3
        return null;
95
    }
96
97
    /**
98
     * @param HydrationStrategyInterface $strategy
99
     * @param object                     $object
100
     * @param string                     $key
101
     * @param mixed                      $value
102
     *
103
     * @throws ReflectionException
104
     *
105
     * @return void
106
     */
107 6
    private function executeStrategy(HydrationStrategyInterface $strategy, object $object, string $key, mixed $value): void
108
    {
109 6
        $mapping = $strategy->getMapping($key);
110 6
        if (!$strategy->isRecursive($key, $value)) {
111 6
            $mapping->setValue($object, $value);
112
113 6
            return;
114
        }
115
116 4
        $type = $mapping->getType()?->getName() ?? stdClass::class;
117 4
        $mapping->setValue($object, $this->hydrate($type, $value));
118 4
    }
119
}
120