Completed
Push — master ( 593f62...4de530 )
by Sébastien
04:11
created

PropertyNormalizer   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 139
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 48
dl 0
loc 139
rs 10
c 1
b 0
f 0
wmc 19

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A instantiate() 0 14 4
A denormalize() 0 39 6
A supports() 0 3 1
B normalize() 0 31 7
1
<?php
2
3
namespace Bdf\Serializer\Normalizer;
4
5
use Bdf\Serializer\Context\DenormalizationContext;
6
use Bdf\Serializer\Context\NormalizationContext;
7
use Bdf\Serializer\Exception\UnexpectedValueException;
8
use Bdf\Serializer\Metadata\MetadataFactoryInterface;
9
use Bdf\Serializer\Type\Type;
10
use Doctrine\Instantiator\Exception\ExceptionInterface;
11
use Doctrine\Instantiator\Instantiator;
12
use Doctrine\Instantiator\InstantiatorInterface;
13
14
/**
15
 * PropertyNormalizer
16
 *
17
 * @author  Seb
18
 */
19
class PropertyNormalizer implements NormalizerInterface
20
{
21
    /**
22
     * The metadata factory
23
     *
24
     * @var MetadataFactoryInterface
25
     */
26
    private $metadataFactory;
27
28
    /**
29
     * The object instantiator
30
     *
31
     * @var InstantiatorInterface
32
     */
33
    private $instantiator;
34
35
    /**
36
     * PropertyNormalizer constructor.
37
     *
38
     * @param MetadataFactoryInterface $metadataFactory
39
     * @param InstantiatorInterface    $instantiator        The instanciator provider. Should returns InstantiatorInterface.
40
     */
41
    public function __construct(MetadataFactoryInterface $metadataFactory, InstantiatorInterface $instantiator = null)
42
    {
43
        $this->metadataFactory = $metadataFactory;
44
        $this->instantiator = $instantiator;
45
    }
46
47
    /**
48
     * {@inheritdoc}
49
     */
50
    public function normalize($object, NormalizationContext $context)
51
    {
52
        $hash = $context->assertNoCircularReference($object);
53
54
        $normalized = [];
55
        $metadata = $this->metadataFactory->getMetadata($object);
56
57
        // TODO Optimize the loop with the options
58
        foreach ($metadata->properties as $property) {
59
            $propertyContext = $context->duplicate($property->normalizationOptions);
60
61
            if ($propertyContext->skipProperty($property)) {
62
                continue;
63
            }
64
65
            $value = $propertyContext->root()->normalize($property->accessor->read($object), $propertyContext);
66
67
            if ($propertyContext->skipPropertyValue($property, $value)) {
68
                continue;
69
            }
70
71
            if ($property->inline && is_array($value) && !$context->includeMetaType()) {
72
                $normalized += $value;
73
            } else {
74
                $normalized[$property->alias] = $value;
75
            }
76
        }
77
78
        $context->releaseReference($hash);
79
80
        return $normalized;
81
    }
82
83
    /**
84
     * {@inheritdoc}
85
     */
86
    public function denormalize($data, Type $type, DenormalizationContext $context)
87
    {
88
        $object = $this->instantiate($type);
89
        $metadata = $this->metadataFactory->getMetadata($object);
90
91
        foreach ((array)$data as $name => $propertyData) {
92
            if (($property = $metadata->property($name)) === null) {
93
                continue;
94
            }
95
96
            $propertyContext = $context->duplicate($property->denormalizationOptions);
97
98
            if ($propertyContext->skipProperty($property)) {
99
                continue;
100
            }
101
102
            // If type is an object we should try to inject
103
            // the new value into the object of the owner object
104
            if (!$property->type->isBuildin()) {
105
                $current = $property->accessor->read($object);
106
107
                // if current is an object we put it on the queue of targets
108
                if (is_object($current)) {
109
                    $property->type->setTarget($current);
110
                }
111
            }
112
113
            $property->accessor->write(
114
                $object,
115
                $propertyContext->root()->denormalize($propertyData, $property->type, $propertyContext)
116
            );
117
118
            // memory leaks
119
            $property->type->setTarget(null);
120
        }
121
122
        $metadata->postDenormalization($object);
123
124
        return $object;
125
    }
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public function supports(string $className): bool
131
    {
132
        return class_exists($className);
133
    }
134
135
    /**
136
     * Instanciate an object
137
     *
138
     * @param Type $type
139
     *
140
     * @return object
141
     *
142
     * @throws UnexpectedValueException  If instanciate could not instanciate type
143
     */
144
    private function instantiate($type)
145
    {
146
        if ($type->target()) {
147
            return $type->target();
148
        }
149
150
        if ($this->instantiator === null) {
151
            $this->instantiator = new Instantiator();
152
        }
153
154
        try {
155
            return $this->instantiator->instantiate($type->name());
156
        } catch (ExceptionInterface $e) {
157
            throw new UnexpectedValueException("Could not instanciate object '".$type->name()."'", 0, $e);
158
        }
159
    }
160
}
161