Completed
Push — master ( 0fb3d7...53ff75 )
by Markus
03:02
created

JmsMetadataParser::doParse()   C

Complexity

Conditions 11
Paths 31

Size

Total Lines 56
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 27
CRAP Score 11.0055

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 56
ccs 27
cts 28
cp 0.9643
rs 6.5481
cc 11
eloc 29
nc 31
nop 3
crap 11.0055

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
namespace Mathielen\ImportEngine\Storage\Parser;
3
4
use JMS\Serializer\Exclusion\GroupsExclusionStrategy;
5
use JMS\Serializer\SerializationContext;
6
use Metadata\MetadataFactoryInterface;
7
use JMS\Serializer\Metadata\PropertyMetadata;
8
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
9
10
/**
11
 * Uses the JMS metadata factory to extract input/output model information
12
 */
13
class JmsMetadataParser
14
{
15
16
    /**
17
     * @var \Metadata\MetadataFactoryInterface
18
     */
19
    private $factory;
20
21
    /**
22
     * @var PropertyNamingStrategyInterface
23
     */
24
    private $namingStrategy;
25
26
    /**
27
     * Constructor, requires JMS Metadata factory
28
     */
29 2
    public function __construct(
30
        MetadataFactoryInterface $factory,
31
        PropertyNamingStrategyInterface $namingStrategy
32
    ) {
33 2
        $this->factory = $factory;
34 2
        $this->namingStrategy = $namingStrategy;
35 2
    }
36
37
    /**
38
     * {@inheritdoc}
39
     */
40 2
    public function parse(array $input)
41
    {
42 2
        $className = $input['class'];
43 2
        $groups    = $input['groups'];
44
45 2
        return $this->doParse($className, array(), $groups);
46
    }
47
48
    /**
49
     * Recursively parse all metadata for a class
50
     *
51
     * @param  string                    $className Class to get all metadata for
52
     * @param  array                     $visited   Classes we've already visited to prevent infinite recursion.
53
     * @param  array                     $groups    Serialization groups to include.
54
     * @return array                     metadata for given class
55
     * @throws \InvalidArgumentException
56
     */
57 2
    protected function doParse($className, $visited = array(), array $groups = array())
58
    {
59 2
        $meta = $this->factory->getMetadataForClass($className);
60
61 2
        if (null === $meta) {
62
            throw new \InvalidArgumentException(sprintf("No metadata found for class %s", $className));
63
        }
64
65 2
        $exclusionStrategies   = array();
66 2
        if ($groups) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $groups 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...
67 1
            $exclusionStrategies[] = new GroupsExclusionStrategy($groups);
68
        }
69
70 2
        $params = array();
71
72
        // iterate over property metadata
73 2
        foreach ($meta->propertyMetadata as $item) {
74 2
            if (!is_null($item->type)) {
75 2
                $name = $this->namingStrategy->translateName($item);
76
77 2
                $dataType = $this->processDataType($item);
78
79
                // apply exclusion strategies
80 2
                foreach ($exclusionStrategies as $strategy) {
81 1
                    if (true === $strategy->shouldSkipProperty($item, SerializationContext::create())) {
82 1
                        continue 2;
83
                    }
84
                }
85
86 2
                $params[$name] = array(
87 2
                    'dataType'     => $dataType['normalized'],
88
                    'required'     => false,
89 2
                    'readonly'     => $item->readOnly,
90 2
                    'sinceVersion' => $item->sinceVersion,
91 2
                    'untilVersion' => $item->untilVersion,
92
                );
93
94 2
                if (!is_null($dataType['class'])) {
95 1
                    $params[$name]['class'] = $dataType['class'];
96
                }
97
98
                // if class already parsed, continue, to avoid infinite recursion
99 2
                if (in_array($dataType['class'], $visited)) {
100 1
                    continue;
101
                }
102
103
                // check for nested classes with JMS metadata
104 2
                if ($dataType['class'] && null !== $this->factory->getMetadataForClass($dataType['class'])) {
105 1
                    $visited[]                 = $dataType['class'];
106 2
                    $params[$name]['children'] = $this->doParse($dataType['class'], $visited, $groups);
107
                }
108
            }
109
        }
110
111 2
        return $params;
112
    }
113
114
    /**
115
     * Figure out a normalized data type (for documentation), and get a
116
     * nested class name, if available.
117
     *
118
     * @return array
119
     */
120 2
    protected function processDataType(PropertyMetadata $item)
121
    {
122
        // check for a type inside something that could be treated as an array
123 2
        if ($nestedType = $this->getNestedTypeInArray($item)) {
124 2
            if ($this->isPrimitive($nestedType)) {
125
                return array(
126 2
                    'normalized' => sprintf("array of %ss", $nestedType),
127
                    'class' => null
128
                );
129
            }
130
131 2
            $exp = explode("\\", $nestedType);
132
133
            return array(
134 2
                'normalized' => sprintf("array of objects (%s)", end($exp)),
135 2
                'class' => $nestedType
136
            );
137
        }
138
139 2
        $type = $item->type['name'];
140
141
        // could be basic type
142 2
        if ($this->isPrimitive($type)) {
143
            return array(
144 1
                'normalized' => $type,
145
                'class' => null
146
            );
147
        }
148
149
        // we can use type property also for custom handlers, then we don't have here real class name
150 2
        if (!class_exists($type)) {
151
            return array(
152 2
                'normalized' => sprintf("custom handler result for (%s)", $type),
153
                'class' => null
154
            );
155
        }
156
157
        // if we got this far, it's a general class name
158 2
        $exp = explode("\\", $type);
159
160
        return array(
161 2
            'normalized' => sprintf("object (%s)", end($exp)),
162 2
            'class' => $type
163
        );
164
    }
165
166 2
    protected function isPrimitive($type)
167
    {
168 2
        return in_array($type, array('boolean', 'integer', 'string', 'float', 'double', 'array', 'DateTime'));
169
    }
170
171
    /**
172
     * Check the various ways JMS describes values in arrays, and
173
     * get the value type in the array
174
     *
175
     * @param  PropertyMetadata $item
176
     * @return string|null
177
     */
178 2
    protected function getNestedTypeInArray(PropertyMetadata $item)
179
    {
180 2
        if (isset($item->type['name']) && in_array($item->type['name'], array('array', 'ArrayCollection'))) {
181 2 View Code Duplication
            if (isset($item->type['params'][1]['name'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
182
                // E.g. array<string, MyNamespaceMyObject>
183 2
                return $item->type['params'][1]['name'];
184
            }
185 2 View Code Duplication
            if (isset($item->type['params'][0]['name'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
186
                // E.g. array<MyNamespaceMyObject>
187 2
                return $item->type['params'][0]['name'];
188
            }
189
        }
190
191 2
        return null;
192
    }
193
194
}
195