Hydrator::isPublic()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4
c 1
b 0
f 0
dl 0
loc 7
rs 10
cc 1
nc 1
nop 1
1
<?php declare(strict_types=1);
2
3
namespace Gravatalonga\Hydrator;
4
5
/**
6
 * Class Hydrator
7
 *
8
 * @package Gravatalonga\Hydrator
9
 */
10
final class Hydrator
11
{
12
    /**
13
     * @var array array<string, mixed>
14
     */
15
    private array $data;
16
17
    private object $object;
18
19
    /**
20
     * Hydrate constructor
21
     *
22
     * @param array array<string, mixed> $data
23
     */
24
    public function __construct(array $data)
25
    {
26
        $this->data = $data;
27
    }
28
29
    /**
30
     * @throws HydratorException
31
     */
32
    public function hydrate(object $object): object
33
    {
34
        $this->object = $object;
35
        foreach ($this->data as $key => $value) {
36
            $this->populateData($key, $value);
37
        }
38
39
        return $this->object;
40
    }
41
42
    /**
43
     * @param string $key
44
     * @param mixed $value
45
     * @throws HydratorException
46
     */
47
    private function populateData(string $key, $value)
48
    {
49
        if (! property_exists($this->object, $key)) {
50
            return;
51
        }
52
53
        if (! $this->isPublic($key)) {
54
            throw HydratorException::cannotPopulateNonPublicProperty($key);
55
        }
56
57
        $this->object->{$key} = $this->formatValue($key, $value);
58
    }
59
60
    private function formatValue(string $key, $value)
61
    {
62
        if ($this->hasCustomFormat($key)) {
63
            return $this->customFormat($key, $value);
64
        }
65
66
        $dataType = $this->dataType($key);
67
68
        if (empty($dataType)) {
69
            return $value;
70
        }
71
72
        if ($dataType === 'int') {
73
            return (int)$value;
74
        }
75
76
        if ($dataType === 'string') {
77
            return (string)$value;
78
        }
79
80
        if ($dataType === 'bool') {
81
            return ! empty($value);
82
        }
83
84
        if ($dataType === 'float') {
85
            return (float)$value;
86
        }
87
88
        return $value;
89
    }
90
91
    private function isPublic(string $key): bool
92
    {
93
        $reflection = new \ReflectionObject($this->object);
94
        $property = $reflection->getProperty($key);
95
        $property->setAccessible(true);
96
97
        return $property->isPublic();
98
    }
99
100
    private function dataType(string $key): string
101
    {
102
        $reflection = new \ReflectionObject($this->object);
103
        $property = $reflection->getProperty($key);
104
        if (is_null($property->getType())) {
105
            return '';
106
        }
107
108
        return ($property->getType())->getName();
0 ignored issues
show
Bug introduced by
The method getName() does not exist on ReflectionType. It seems like you code against a sub-type of ReflectionType such as ReflectionNamedType. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

108
        return ($property->getType())->/** @scrutinizer ignore-call */ getName();
Loading history...
109
    }
110
111
    private function hasCustomFormat(string $key): bool
112
    {
113
        return method_exists($this->object, $this->methodNameFormat($key));
114
    }
115
116
    private function customFormat(string $key, $value)
117
    {
118
        return $this->object->{$this->methodNameFormat($key)}($value);
119
    }
120
121
    private function methodNameFormat(string $propertyName): string
122
    {
123
        return sprintf('format%s', ucfirst($propertyName));
124
    }
125
}
126