Completed
Pull Request — master (#743)
by Asmir
03:52
created

ClassMetadata::merge()   C

Complexity

Conditions 14
Paths 67

Size

Total Lines 78
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 39
CRAP Score 18.2018

Importance

Changes 0
Metric Value
dl 0
loc 78
ccs 39
cts 54
cp 0.7221
rs 5.1581
c 0
b 0
f 0
cc 14
eloc 53
nc 67
nop 1
crap 18.2018

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
/*
4
 * Copyright 2016 Johannes M. Schmitt <[email protected]>
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 *     http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
namespace JMS\Serializer\Metadata;
20
21
use JMS\Serializer\Exception\InvalidArgumentException;
22
use Metadata\MergeableClassMetadata;
23
use Metadata\MergeableInterface;
24
use Metadata\MethodMetadata;
25
use Metadata\PropertyMetadata as BasePropertyMetadata;
26
27
/**
28
 * Class Metadata used to customize the serialization process.
29
 *
30
 * @author Johannes M. Schmitt <[email protected]>
31
 */
32
class ClassMetadata extends MergeableClassMetadata
33
{
34
    const ACCESSOR_ORDER_UNDEFINED = 'undefined';
35
    const ACCESSOR_ORDER_ALPHABETICAL = 'alphabetical';
36
    const ACCESSOR_ORDER_CUSTOM = 'custom';
37
38
    /** @var \ReflectionMethod[] */
39
    public $preSerializeMethods = array();
40
41
    /** @var \ReflectionMethod[] */
42
    public $postSerializeMethods = array();
43
44
    /** @var \ReflectionMethod[] */
45
    public $postDeserializeMethods = array();
46
47
    public $xmlRootName;
48
    public $xmlRootNamespace;
49
    public $xmlNamespaces = array();
50
    public $accessorOrder;
51
    public $customOrder;
52
    public $usingExpression = false;
53
54
55
    public $discriminatorDisabled = false;
56
    public $discriminatorBaseClass;
57
    public $discriminatorFieldName;
58
    public $discriminatorValue;
59
    public $discriminatorMap = array();
60
    public $discriminatorGroups = array();
61
62
    public $xmlDiscriminatorAttribute = false;
63
    public $xmlDiscriminatorCData = true;
64
    public $xmlDiscriminatorNamespace;
65
66 33
    public function setDiscriminator($fieldName, array $map, array $groups = array())
67
    {
68 33
        if (empty($fieldName)) {
69
            throw new \InvalidArgumentException('The $fieldName cannot be empty.');
70
        }
71
72 33
        if (empty($map)) {
73
            throw new \InvalidArgumentException('The discriminator map cannot be empty.');
74
        }
75
76 33
        $this->discriminatorBaseClass = $this->name;
77 33
        $this->discriminatorFieldName = $fieldName;
78 33
        $this->discriminatorMap = $map;
79 33
        $this->discriminatorGroups = $groups;
80 33
    }
81
82
    /**
83
     * Sets the order of properties in the class.
84
     *
85
     * @param string $order
86
     * @param array $customOrder
87
     *
88
     * @throws InvalidArgumentException When the accessor order is not valid
89
     * @throws InvalidArgumentException When the custom order is not valid
90
     */
91 53
    public function setAccessorOrder($order, array $customOrder = array())
92
    {
93 53
        if (!in_array($order, array(self::ACCESSOR_ORDER_UNDEFINED, self::ACCESSOR_ORDER_ALPHABETICAL, self::ACCESSOR_ORDER_CUSTOM), true)) {
94
            throw new InvalidArgumentException(sprintf('The accessor order "%s" is invalid.', $order));
95
        }
96
97 53
        foreach ($customOrder as $name) {
98 46
            if (!\is_string($name)) {
99 46
                throw new InvalidArgumentException(sprintf('$customOrder is expected to be a list of strings, but got element of value %s.', json_encode($name)));
100
            }
101
        }
102
103 53
        $this->accessorOrder = $order;
104 53
        $this->customOrder = array_flip($customOrder);
105 53
        $this->sortProperties();
106 53
    }
107
108 306
    public function addPropertyMetadata(BasePropertyMetadata $metadata)
109
    {
110 306
        parent::addPropertyMetadata($metadata);
111 306
        $this->sortProperties();
112 306
        if ($metadata instanceof PropertyMetadata && $metadata->excludeIf) {
113 36
            $this->usingExpression = true;
114
        }
115 306
    }
116
117 3
    public function addPreSerializeMethod(MethodMetadata $method)
118
    {
119 3
        $this->preSerializeMethods[] = $method;
120 3
    }
121
122 3
    public function addPostSerializeMethod(MethodMetadata $method)
123
    {
124 3
        $this->postSerializeMethods[] = $method;
125 3
    }
126
127 6
    public function addPostDeserializeMethod(MethodMetadata $method)
128
    {
129 6
        $this->postDeserializeMethods[] = $method;
130 6
    }
131
132 31
    public function merge(MergeableInterface $object)
133
    {
134 31
        if (!$object instanceof ClassMetadata) {
135
            throw new InvalidArgumentException('$object must be an instance of ClassMetadata.');
136
        }
137 31
        parent::merge($object);
138
139 31
        $this->preSerializeMethods = array_merge($this->preSerializeMethods, $object->preSerializeMethods);
140 31
        $this->postSerializeMethods = array_merge($this->postSerializeMethods, $object->postSerializeMethods);
141 31
        $this->postDeserializeMethods = array_merge($this->postDeserializeMethods, $object->postDeserializeMethods);
142 31
        $this->xmlRootName = $object->xmlRootName;
143 31
        $this->xmlRootNamespace = $object->xmlRootNamespace;
144 31
        $this->xmlNamespaces = array_merge($this->xmlNamespaces, $object->xmlNamespaces);
145
146 31
        if ($object->accessorOrder) {
147 3
            $this->accessorOrder = $object->accessorOrder;
148 3
            $this->customOrder = $object->customOrder;
149
        }
150
151 31
        if ($object->discriminatorFieldName && $this->discriminatorFieldName) {
152
            throw new \LogicException(sprintf(
153
                'The discriminator of class "%s" would overwrite the discriminator of the parent class "%s". Please define all possible sub-classes in the discriminator of %s.',
154
                $object->name,
155
                $this->discriminatorBaseClass,
156
                $this->discriminatorBaseClass
157
            ));
158 31
        } elseif (!$this->discriminatorFieldName && $object->discriminatorFieldName) {
159 2
            $this->discriminatorFieldName = $object->discriminatorFieldName;
160 2
            $this->discriminatorMap = $object->discriminatorMap;
161
        }
162
163 31
        if ($object->discriminatorDisabled !== null) {
164 31
            $this->discriminatorDisabled = $object->discriminatorDisabled;
165
        }
166
167 31
        if ($object->discriminatorMap) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $object->discriminatorMap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
168 2
            $this->discriminatorFieldName = $object->discriminatorFieldName;
169 2
            $this->discriminatorMap = $object->discriminatorMap;
170 2
            $this->discriminatorBaseClass = $object->discriminatorBaseClass;
171
        }
172
173 31
        if ($this->discriminatorMap && !$this->reflection->isAbstract()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->discriminatorMap of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
174 18
            if (false === $typeValue = array_search($this->name, $this->discriminatorMap, true)) {
175
                throw new \LogicException(sprintf(
176
                    'The sub-class "%s" is not listed in the discriminator of the base class "%s".',
177
                    $this->name,
178
                    $this->discriminatorBaseClass
179
                ));
180
            }
181
182 18
            $this->discriminatorValue = $typeValue;
183
184 18
            if (isset($this->propertyMetadata[$this->discriminatorFieldName])
185 18
                && !$this->propertyMetadata[$this->discriminatorFieldName] instanceof StaticPropertyMetadata
186
            ) {
187
                throw new \LogicException(sprintf(
188
                    'The discriminator field name "%s" of the base-class "%s" conflicts with a regular property of the sub-class "%s".',
189
                    $this->discriminatorFieldName,
190
                    $this->discriminatorBaseClass,
191
                    $this->name
192
                ));
193
            }
194
195 18
            $discriminatorProperty = new StaticPropertyMetadata(
196 18
                $this->name,
197 18
                $this->discriminatorFieldName,
198 18
                $typeValue,
199 18
                $this->discriminatorGroups
200
            );
201 18
            $discriminatorProperty->serializedName = $this->discriminatorFieldName;
202 18
            $discriminatorProperty->xmlAttribute = $this->xmlDiscriminatorAttribute;
203 18
            $discriminatorProperty->xmlElementCData = $this->xmlDiscriminatorCData;
204 18
            $discriminatorProperty->xmlNamespace = $this->xmlDiscriminatorNamespace;
205 18
            $this->propertyMetadata[$this->discriminatorFieldName] = $discriminatorProperty;
206
        }
207
208 31
        $this->sortProperties();
209 31
    }
210
211 30
    public function registerNamespace($uri, $prefix = null)
212
    {
213 30
        if (!\is_string($uri)) {
214
            throw new InvalidArgumentException(sprintf('$uri is expected to be a strings, but got value %s.', json_encode($uri)));
215
        }
216
217 30
        if ($prefix !== null) {
218 30
            if (!\is_string($prefix)) {
219 30
                throw new InvalidArgumentException(sprintf('$prefix is expected to be a strings, but got value %s.', json_encode($prefix)));
220
            }
221
        } else {
222 2
            $prefix = "";
223
        }
224
225 30
        $this->xmlNamespaces[$prefix] = $uri;
226
227 30
    }
228
229
    public function serialize()
230
    {
231
        $this->sortProperties();
232
233
        return serialize(array(
234
            $this->preSerializeMethods,
235
            $this->postSerializeMethods,
236
            $this->postDeserializeMethods,
237
            $this->xmlRootName,
238
            $this->xmlRootNamespace,
239
            $this->xmlNamespaces,
240
            $this->accessorOrder,
241
            $this->customOrder,
242
            $this->discriminatorDisabled,
243
            $this->discriminatorBaseClass,
244
            $this->discriminatorFieldName,
245
            $this->discriminatorValue,
246
            $this->discriminatorMap,
247
            $this->discriminatorGroups,
248
            parent::serialize(),
249
            'discriminatorGroups' => $this->discriminatorGroups,
250
            'xmlDiscriminatorAttribute' => $this->xmlDiscriminatorAttribute,
251
            'xmlDiscriminatorCData' => $this->xmlDiscriminatorCData,
252
            'usingExpression' => $this->usingExpression,
253
            'xmlDiscriminatorNamespace' => $this->xmlDiscriminatorNamespace,
254
        ));
255
    }
256
257
    public function unserialize($str)
258
    {
259
        $unserialized = unserialize($str);
260
261
        list(
262
            $this->preSerializeMethods,
263
            $this->postSerializeMethods,
264
            $this->postDeserializeMethods,
265
            $this->xmlRootName,
266
            $this->xmlRootNamespace,
267
            $this->xmlNamespaces,
268
            $this->accessorOrder,
269
            $this->customOrder,
270
            $this->discriminatorDisabled,
271
            $this->discriminatorBaseClass,
272
            $this->discriminatorFieldName,
273
            $this->discriminatorValue,
274
            $this->discriminatorMap,
275
            $this->discriminatorGroups,
276
            $parentStr
277
            ) = $unserialized;
278
279
        if (isset($unserialized['discriminatorGroups'])) {
280
            $this->discriminatorGroups = $unserialized['discriminatorGroups'];
281
        }
282
        if (isset($unserialized['usingExpression'])) {
283
            $this->usingExpression = $unserialized['usingExpression'];
284
        }
285
286
        if (isset($unserialized['xmlDiscriminatorAttribute'])) {
287
            $this->xmlDiscriminatorAttribute = $unserialized['xmlDiscriminatorAttribute'];
288
        }
289
290
        if (isset($unserialized['xmlDiscriminatorNamespace'])) {
291
            $this->xmlDiscriminatorNamespace = $unserialized['xmlDiscriminatorNamespace'];
292
        }
293
294
        if (isset($unserialized['xmlDiscriminatorCData'])) {
295
            $this->xmlDiscriminatorCData = $unserialized['xmlDiscriminatorCData'];
296
        }
297
298
        parent::unserialize($parentStr);
299
    }
300
301 309
    private function sortProperties()
302
    {
303 309
        switch ($this->accessorOrder) {
304 309
            case self::ACCESSOR_ORDER_ALPHABETICAL:
305 10
                ksort($this->propertyMetadata);
306 10
                break;
307
308 306
            case self::ACCESSOR_ORDER_CUSTOM:
309 46
                $order = $this->customOrder;
310 46
                $currentSorting = $this->propertyMetadata ? array_combine(array_keys($this->propertyMetadata), range(1, \count($this->propertyMetadata))) : [];
311 46
                uksort($this->propertyMetadata, function ($a, $b) use ($order, $currentSorting) {
312 46
                    $existsA = isset($order[$a]);
313 46
                    $existsB = isset($order[$b]);
314
315 46
                    if (!$existsA && !$existsB) {
316 5
                        return $currentSorting[$a] - $currentSorting[$b];
317
                    }
318
319 45
                    if (!$existsA) {
320 5
                        return 1;
321
                    }
322
323 40
                    if (!$existsB) {
324 1
                        return -1;
325
                    }
326
327 39
                    return $order[$a] < $order[$b] ? -1 : 1;
328 46
                });
329 46
                break;
330
        }
331 309
    }
332
}
333