Completed
Push — master ( 40e412...07993c )
by Tobias
02:30
created

MetadataAwareNormalizer::getMetadata()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 6
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
1
<?php
2
3
namespace Happyr\SerializerBundle\Normalizer;
4
5
use Happyr\SerializerBundle\Annotation\ExclusionPolicy;
6
use Happyr\SerializerBundle\Metadata\Metadata;
7
use Happyr\SerializerBundle\PropertyManager\AttributeExtractor;
8
use Happyr\SerializerBundle\PropertyManager\ReflectionPropertyAccess;
9
use Happyr\SerializerBundle\PropertyManager\PropertyNameConverter;
10
use Symfony\Component\Serializer\Exception\LogicException;
11
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
12
use Symfony\Component\Serializer\Normalizer\SerializerAwareNormalizer;
13
14
/**
15
 * @author Tobias Nyholm <[email protected]>
16
 */
17
class MetadataAwareNormalizer extends SerializerAwareNormalizer implements NormalizerInterface
0 ignored issues
show
Deprecated Code introduced by
The class Symfony\Component\Serial...rializerAwareNormalizer has been deprecated with message: since version 3.1, to be removed in 4.0. Use the SerializerAwareTrait instead.

This class, trait or interface has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the type will be removed from the class and what other constant to use instead.

Loading history...
18
{
19
    use GroupValidationTrait;
20
21
    /**
22
     * @var Metadata[]
23
     */
24
    private $metadata;
25
26
    /**
27
     * @var AttributeExtractor
28
     */
29
    private $attributeExtractor;
30
31
    /**
32
     * @var PropertyNameConverter
33
     */
34
    private $propertyNameConverter;
35
36
    /**
37
     * @param array                 $metadata
38
     * @param AttributeExtractor    $attributeExtractor
39
     * @param PropertyNameConverter $pnc
40
     */
41
    public function __construct(array $metadata, AttributeExtractor $attributeExtractor, PropertyNameConverter $pnc)
42
    {
43
        $this->metadata = $metadata;
44
        $this->attributeExtractor = $attributeExtractor;
45
        $this->propertyNameConverter = $pnc;
46
    }
47
48
    public function normalize($object, $format = null, array $context = array())
49
    {
50
        $meta = $this->getMetadata($object);
51
        $attributes = $this->attributeExtractor->getAttributes($object);
52
53
        $normalizedData = [];
54
        foreach ($attributes['property'] as $propertyName => $bool) {
0 ignored issues
show
Bug introduced by
The expression $attributes['property'] of type string is not traversable.
Loading history...
55
            $this->normalizeProperty($normalizedData, $meta, $object, $propertyName, $context);
0 ignored issues
show
Documentation introduced by
$meta is of type object<Happyr\SerializerBundle\Metadata\Metadata>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
56
        }
57
        foreach ($attributes['method'] as $propertyName => $bool) {
0 ignored issues
show
Bug introduced by
The expression $attributes['method'] of type string is not traversable.
Loading history...
58
            $this->normalizeMethod($normalizedData, $meta, $object, $propertyName, $context);
0 ignored issues
show
Documentation introduced by
$meta is of type object<Happyr\SerializerBundle\Metadata\Metadata>, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
59
        }
60
61
        foreach ($normalizedData as $name => $value) {
62
            if (null !== $value && !is_scalar($value)) {
63
                if (!$this->serializer instanceof NormalizerInterface) {
64
                    throw new LogicException(sprintf('Cannot normalize attribute "%s" because injected serializer is not a normalizer', $name));
65
                }
66
67
                $normalizedData[$name] = $this->serializer->normalize($value, $format, $context);
68
            }
69
        }
70
71
        return $normalizedData;
72
    }
73
74
    protected function normalizeProperty(array &$normalizedData, array $meta, $object, $propertyName, array $context)
75
    {
76
        if (!isset($meta['property'][$propertyName])) {
77
            $meta['property'][$propertyName] = [];
78
        }
79
80
        // Default exclusion policy is NONE
81
        $exclusionPolicy = isset($meta['class']['exclusion_policy']) ? $meta['class']['exclusion_policy'] : ExclusionPolicy::NONE;
82
83
        // If this property should be in the output
84
        $included = $exclusionPolicy === ExclusionPolicy::NONE;
85
86
        $groups = ['Default'];
87
        $value = ReflectionPropertyAccess::get($object, $propertyName);
88
        foreach ($meta['property'][$propertyName] as $name => $metaValue) {
89
            switch ($name) {
90
                case 'exclude';
0 ignored issues
show
Coding Style introduced by
CASE statements must be defined using a colon

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
91
                    // Skip this
92
                    return;
93
                case 'expose':
94
                    $included = true;
95
                    break;
96
                case 'accessor':
97
                    if (!empty($metaValue['getter'])) {
98
                        $accessor = $metaValue['getter'];
99
                        $value = $object->$accessor();
100
                    }
101
                    break;
102
                case 'groups':
103
                    $groups = $metaValue;
104
                    break;
105
            }
106
        }
107
108
        // Validate context groups
109
        if (!empty($context['groups'])) {
110
            $included = $this->includeBasedOnGroup($context, $groups);
111
        }
112
113
        if (!$included) {
114
            return;
115
        }
116
117
        $serializedName = $this->propertyNameConverter->getSerializedName($meta['property'][$propertyName], $propertyName);
118
        $normalizedData[$serializedName] = $value;
119
    }
120
121
    protected function normalizeMethod(array &$normalizedData, array $meta, $object, $methodName, array $context)
122
    {
123
        if (!isset($meta['method'][$methodName])) {
124
            $meta['method'][$methodName] = [];
125
        }
126
127
        // Methods are never serialized by default
128
        $included = false;
129
130
        $groups = ['Default'];
131
        foreach ($meta['method'][$methodName] as $name => $metaValue) {
132
            switch ($name) {
133
                case 'expose':
134
                    $included = true;
135
                    break;
136
                case 'serialized_name':
137
                    $serializedName = $metaValue;
0 ignored issues
show
Unused Code introduced by
$serializedName is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
138
                    break;
139
                case 'groups':
140
                    $groups = $metaValue;
141
                    break;
142
            }
143
        }
144
145
        // Validate context groups
146
        if (!empty($context['groups'])) {
147
            $included = $this->includeBasedOnGroup($context, $groups);
148
        }
149
150
        if (!$included) {
151
            return;
152
        }
153
154
        $serializedName = $this->propertyNameConverter->getSerializedName($meta['method'][$methodName], $methodName);
155
        $normalizedData[$serializedName] = $object->$methodName();
156
    }
157
158
    /**
159
     * @param mixed $data
160
     * @param null  $format
161
     *
162
     * @return bool
163
     */
164
    public function supportsNormalization($data, $format = null)
165
    {
166
        $class = get_class($data);
167
168
        return isset($this->metadata[$class]);
169
    }
170
171
    /**
172
     * @param $object
173
     */
174
    private function getMetadata($object)
175
    {
176
        $class = get_class($object);
177
178
        return $this->metadata[$class];
179
    }
180
}
181