Normalizer   A
last analyzed

Complexity

Total Complexity 29

Size/Duplication

Total Lines 125
Duplicated Lines 0 %

Test Coverage

Coverage 96.43%

Importance

Changes 0
Metric Value
wmc 29
dl 0
loc 125
ccs 54
cts 56
cp 0.9643
rs 10
c 0
b 0
f 0

8 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A normalize() 0 9 2
A normalizeIfSupported() 0 11 3
A supportsNormalization() 0 3 2
A clear() 0 3 1
A convertDateTimeValue() 0 3 1
A setNormalizer() 0 3 1
C extractData() 0 58 18
1
<?php
2
3
namespace Vox\Serializer;
4
5
use Metadata\MetadataFactoryInterface;
6
use RuntimeException;
7
use Symfony\Component\Serializer\Normalizer\NormalizerAwareInterface;
8
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
9
use Vox\Data\Mapping\Bindings;
10
use Vox\Data\Mapping\Exclude;
11
use Vox\Metadata\ClassMetadata;
12
use Vox\Metadata\PropertyMetadata;
13
14
/**
15
 * A data normalized aimed to be used with symfony serializer component
16
 * 
17
 * @author Jhonatan Teixeira <[email protected]>
18
 */
19
class Normalizer implements NormalizerInterface, NormalizerAwareInterface
20
{
21
    /**
22
     * @var MetadataFactoryInterface
23
     */
24
    private $metadataFactory;
25
26
    /**
27
     * @var NormalizerInterface
28
     */
29
    private $normalizer;
30
31
    /**
32
     * @var \SplObjectStorage
33
     */
34
    private $storage;
35
    
36 45
    public function __construct(MetadataFactoryInterface $metadataFactory)
37
    {
38 45
        $this->metadataFactory = $metadataFactory;
39 45
        $this->clear();
40 45
    }
41
    
42 15
    public function normalize($object, $format = null, array $context = [])
43
    {
44 15
        if ($this->storage->offsetExists($object)) {
45
            return $this->storage[$object];
46
        }
47
48 15
        $data = $this->extractData($object, $format, $context);
49
50 15
        return $data;
51
    }
52
53 15
    private function extractData($object, string $format = null, array $context = []): array
54
    {
55 15
        $objectMetadata = $this->metadataFactory->getMetadataForClass(get_class($object));
56
57 15
        if (!$objectMetadata instanceof ClassMetadata) {
58
            throw new RuntimeException('invalid metadata class');
59
        }
60
61 15
        $data = [];
62
63 15
        if ($objectMetadata->reflection->getParentClass() && ($context['exposeTypes'] ?? true)) {
64 1
            $data['type'] = get_class($object);
65
        }
66
67
        /* @var $propertyMetadata PropertyMetadata */
68 15
        foreach ($objectMetadata->propertyMetadata as $propertyMetadata) {
69 15
            $binding = $propertyMetadata->getAnnotation(Bindings::class);
70 15
            $value   = $propertyMetadata->getValue($object);
71
72 15
            if ($propertyMetadata->hasAnnotation(Exclude::class)
73 15
                && $propertyMetadata->getAnnotation(Exclude::class)->output) {
74 2
                continue;
75
            }
76
            
77 15
            if (!empty($value) && $propertyMetadata->isDateType() && $propertyMetadata->isDecoratedType()) {
78 1
                $value = $this->convertDateTimeValue($value, $propertyMetadata);
79
            }
80
81 15
            if ((is_array($value) || $value instanceof \Traversable)
82 15
                && (preg_match('/\[\]$/', $propertyMetadata->type) || $propertyMetadata->type == 'array')) {
83 2
                $items = [];
84
85 2
                foreach ($value as $index => $item) {
86 2
                    $items[$index] = $this->normalizeIfSupported($item, $format, $context);
87
                }
88
89 2
                $value = $items;
90
            }
91
92 15
            if (is_object($value)) {
93 6
                $value = $this->normalizeIfSupported($value, $format, $context);
94
            }
95
96 15
            $target = $binding
97 5
                ? ($binding->target ?? $binding->source ?? $propertyMetadata->name)
98 15
                : $propertyMetadata->name;
99
100 15
            if (preg_match('/\./', $target)) {
101 2
                $path = implode("']['", explode('.', sprintf("['%s']", $target)));
102 2
                eval("\$data$path = \$value;");
0 ignored issues
show
introduced by
The use of eval() is discouraged.
Loading history...
103
            } else {
104 15
                $data[$target] = $value;
105
            }
106
        }
107
108 15
        $this->storage[$object] = $data;
109
110 15
        return $data;
111
    }
112
    
113 1
    private function convertDateTimeValue(\DateTime $date, PropertyMetadata $propertyMetadata): string
114
    {
115 1
        return $date->format($propertyMetadata->typeInfo['decoration'] ?? 'Y-m-d H:i:s');
116
    }
117
    
118 7
    private function normalizeIfSupported($value, string $format = null, array $context = [])
119
    {
120 7
        if (isset($this->normalizer)) {
121 5
            return $this->normalizer->normalize($value, $format, $context);
122
        }
123
124 2
        if ($this->supportsNormalization($value)) {
125 2
            return $this->normalize($value, $format, $context);
126
        }
127
        
128 1
        return $value;
129
    }
130
131 14
    public function supportsNormalization($data, $format = null): bool
132
    {
133 14
        return is_object($data) && !$data instanceof \DateTime;
134
    }
135
136 42
    public function setNormalizer(NormalizerInterface $normalizer)
137
    {
138 42
        $this->normalizer = $normalizer;
139 42
    }
140
141 45
    public function clear()
142
    {
143 45
        $this->storage = new \SplObjectStorage();
144 45
    }
145
}
146