Completed
Push — master ( 31fba3...844081 )
by JHONATAN
02:27
created

ObjectHydrator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 1
nc 1
nop 1
crap 1
1
<?php
2
3
namespace Vox\Data;
4
5
use DateTime;
6
use Metadata\MetadataFactoryInterface;
7
use RuntimeException;
8
use Vox\Data\Mapping\Bindings;
9
use Vox\Data\Mapping\Discriminator;
10
use Vox\Metadata\ClassMetadata;
11
use Vox\Metadata\PropertyMetadata;
12
13
class ObjectHydrator implements ObjectHydratorInterface
14
{
15
    /**
16
     * @var MetadataFactoryInterface
17
     */
18
    private $metadataFactory;
19
    
20 12
    public function __construct(MetadataFactoryInterface $metadataFactory)
21
    {
22 12
        $this->metadataFactory = $metadataFactory;
23 12
    }
24
    
25 12
    public function hydrate($object, array $data)
26
    {
27 12
        $objectMetadata = $this->getObjectMetadata($object, $data);
28
29
        /* @var $propertyMetadata PropertyMetadata  */
30 12
        foreach ($objectMetadata->propertyMetadata as $propertyMetadata) {
31 12
            $annotation = $propertyMetadata->getAnnotation(Bindings::class);
32 12
            $source     = $annotation ? ($annotation->source ?? $propertyMetadata->name) : $propertyMetadata->name;
33 12
            $type       = $propertyMetadata->type;
34
            
35 12
            if (!isset($data[$source])) {
36 9
                continue;
37
            }
38
            
39 12
            $value = $data[$source];
40
            
41 12
            if ($type && $value) {
42 7
                if ($this->isDecorated($type)) {
43 2
                    $value = $this->convertDecorated($type, $value);
44 6
                } elseif ($this->isNativeType($type)) {
45 6
                    $value = $this->convertNativeType($type, $value);
46
                } else {
47 4
                    $value = $this->convertObjectValue($type, $value);
48
                }
49
            }
50
51 12
            $propertyMetadata->setValue($object, $value);
52
        }
53 12
    }
54
    
55 6
    private function isNativeType(string $type)
56
    {
57 6
        return in_array($type, [
58 6
            'string',
59
            'array',
60
            'int',
61
            'integer',
62
            'float',
63
            'boolean',
64
            'bool',
65
            'DateTime',
66
            '\DateTime',
67
        ]);
68
    }
69
    
70 7
    private function isDecorated(string $type): bool
71
    {
72 7
        return (bool) preg_match('/(.*)((\<(.*)\>)|(\[\]))/', $type);
73
    }
74
    
75 6
    private function convertNativeType($type, $value)
76
    {
77
        switch ($type) {
78 6
            case 'int':
79 5
            case 'integer':
80 6
                return (int) $value;
81 5
            case 'string':
82 5
                return (string) $value;
83
            case 'boolean':
84
            case 'bool':
85
                return (bool) $value;
86
            case 'array':
87
                if (!is_array($value)) {
88
                    throw new RuntimeException('value is not array');
89
                }
90
                
91
                return $value;
92
            case 'DateTime':
93
            case '\DateTime':
94
                return new DateTime($value);
95
            default:
96
                return $value;
97
        }
98
    }
99
    
100 2
    private function convertDecorated(string $type, $value)
101
    {
102 2
        preg_match('/(?P<class>.*)((\<(?P<decoration>.*)\>)|(?P<brackets>\[\]))/', $type, $matches);
103
        
104 2
        $class      = isset($matches['brackets']) ? 'array' : $matches['class'];
105 2
        $decoration = isset($matches['brackets']) ? $matches['class'] : $matches['decoration'];
106
107
        switch ($class) {
108 2
            case 'array':
109 2
                if (!is_array($value)) {
110
                    throw new RuntimeException('value mapped as array is not array');
111
                }
112
113 2
                $data = [];
114
115 2
                foreach ($value as $item) {
116 2
                    $object = $this->convertObjectValue($decoration, $item);
117
118 2
                    $data[] = $object;
119
                }
120
121 2
                break;
122 1
            case 'DateTime':
123
            case '\DateTime':
124 1
                $data = DateTime::createFromFormat($decoration, $value);
125 1
                break;
126
        }
127
128 2
        return $data;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.
Loading history...
129
    }
130
    
131 5
    private function convertObjectValue(string $type, array $data)
132
    {
133 5
        $metadata = $this->getObjectMetadata($type, $data);
134 5
        $object   = $metadata->reflection->newInstanceWithoutConstructor();
135
136 5
        $this->hydrate(
137 5
            $object, 
138 5
            $data
139
        );
140
141 5
        return $object;
142
    }
143
    
144 12
    private function getObjectMetadata($object, array $data): ClassMetadata
145
    {
146 12
        $metadata      = $this->metadataFactory->getMetadataForClass(is_string($object) ? $object : get_class($object));
147 12
        $discriminator = $metadata->getAnnotation(Discriminator::class);
0 ignored issues
show
Bug introduced by
The method getAnnotation() does not exist on Metadata\ClassHierarchyMetadata. ( Ignorable by Annotation )

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

147
        /** @scrutinizer ignore-call */ 
148
        $discriminator = $metadata->getAnnotation(Discriminator::class);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
introduced by
The method getAnnotation() does not exist on Metadata\MergeableClassMetadata. Are you sure you never get this type here, but always one of the subclasses? ( Ignorable by Annotation )

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

147
        /** @scrutinizer ignore-call */ 
148
        $discriminator = $metadata->getAnnotation(Discriminator::class);
Loading history...
148
149 12
        if ($discriminator instanceof Discriminator && isset($data[$discriminator->field])) {
150 3
            if (!isset($discriminator->map[$data[$discriminator->field]])) {
151
                throw new RuntimeException("no discrimination for {$data[$discriminator->field]}");
152
            }
153
154 3
            $type     = $discriminator->map[$data[$discriminator->field]];
155 3
            $metadata = $this->metadataFactory->getMetadataForClass($type);
156
        }
157
        
158 12
        return $metadata;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $metadata returns the type null|Metadata\ClassHierarchyMetadata which is incompatible with the type-hinted return Vox\Metadata\ClassMetadata.
Loading history...
159
    }
160
}
161