Completed
Pull Request — master (#708)
by Asmir
11:37
created

AnnotationDriver   F

Complexity

Total Complexity 58

Size/Duplication

Total Lines 184
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 32

Test Coverage

Coverage 97.45%

Importance

Changes 0
Metric Value
wmc 58
lcom 1
cbo 32
dl 0
loc 184
ccs 153
cts 157
cp 0.9745
rs 1.3043
c 0
b 0
f 0

2 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
F loadMetadataForClass() 0 174 57

How to fix   Complexity   

Complex Class

Complex classes like AnnotationDriver often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use AnnotationDriver, and based on these observations, apply Extract Interface, too.

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\Driver;
20
21
use JMS\Serializer\Annotation\Discriminator;
22
use JMS\Serializer\Annotation\ExcludeIf;
23
use JMS\Serializer\Annotation\XmlDiscriminator;
24
use JMS\Serializer\GraphNavigator;
25
use JMS\Serializer\Annotation\HandlerCallback;
26
use JMS\Serializer\Annotation\AccessorOrder;
27
use JMS\Serializer\Annotation\Accessor;
28
use JMS\Serializer\Annotation\AccessType;
29
use JMS\Serializer\Annotation\XmlMap;
30
use JMS\Serializer\Annotation\XmlRoot;
31
use JMS\Serializer\Annotation\XmlNamespace;
32
use JMS\Serializer\Annotation\XmlAttribute;
33
use JMS\Serializer\Annotation\XmlList;
34
use JMS\Serializer\Annotation\XmlValue;
35
use JMS\Serializer\Annotation\XmlKeyValuePairs;
36
use JMS\Serializer\Annotation\XmlElement;
37
use JMS\Serializer\Annotation\PostSerialize;
38
use JMS\Serializer\Annotation\PostDeserialize;
39
use JMS\Serializer\Annotation\PreSerialize;
40
use JMS\Serializer\Annotation\VirtualProperty;
41
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
42
use Metadata\MethodMetadata;
43
use Doctrine\Common\Annotations\Reader;
44
use JMS\Serializer\Annotation\Type;
45
use JMS\Serializer\Annotation\Exclude;
46
use JMS\Serializer\Annotation\Groups;
47
use JMS\Serializer\Annotation\Expose;
48
use JMS\Serializer\Annotation\SerializedName;
49
use JMS\Serializer\Annotation\Until;
50
use JMS\Serializer\Annotation\Since;
51
use JMS\Serializer\Annotation\ExclusionPolicy;
52
use JMS\Serializer\Annotation\Inline;
53
use JMS\Serializer\Annotation\ReadOnly;
54
use JMS\Serializer\Metadata\ClassMetadata;
55
use JMS\Serializer\Metadata\PropertyMetadata;
56
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
57
use JMS\Serializer\Exception\InvalidArgumentException;
58
use JMS\Serializer\Annotation\XmlAttributeMap;
59
use Metadata\Driver\DriverInterface;
60
use JMS\Serializer\Annotation\MaxDepth;
61
62
class AnnotationDriver implements DriverInterface
63
{
64
    private $reader;
65
66 354
    public function __construct(Reader $reader)
67
    {
68 354
        $this->reader = $reader;
69 354
    }
70
71 234
    public function loadMetadataForClass(\ReflectionClass $class)
72
    {
73 234
        $classMetadata = new ClassMetadata($name = $class->name);
74 234
        $classMetadata->fileResources[] = $class->getFilename();
75
76 234
        $propertiesMetadata = array();
77 234
        $propertiesAnnotations = array();
78
79 234
        $exclusionPolicy = 'NONE';
80 234
        $excludeAll = false;
81 234
        $classAccessType = PropertyMetadata::ACCESS_TYPE_PROPERTY;
82 234
        $readOnlyClass = false;
83 234
        foreach ($this->reader->getClassAnnotations($class) as $annot) {
84 137
            if ($annot instanceof ExclusionPolicy) {
85 34
                $exclusionPolicy = $annot->policy;
86 137
            } elseif ($annot instanceof XmlRoot) {
87 58
                $classMetadata->xmlRootName = $annot->name;
88 58
                $classMetadata->xmlRootNamespace = $annot->namespace;
89 136
            } elseif ($annot instanceof XmlNamespace) {
90 15
                $classMetadata->registerNamespace($annot->uri, $annot->prefix);
91 102
            } elseif ($annot instanceof Exclude) {
92
                $excludeAll = true;
93 87
            } elseif ($annot instanceof AccessType) {
94 3
                $classAccessType = $annot->type;
95 87
            } elseif ($annot instanceof ReadOnly) {
96 3
                $readOnlyClass = true;
97 84
            } elseif ($annot instanceof AccessorOrder) {
98 46
                $classMetadata->setAccessorOrder($annot->order, $annot->custom);
99 81
            } elseif ($annot instanceof Discriminator) {
100 19
                if ($annot->disabled) {
101
                    $classMetadata->discriminatorDisabled = true;
102
                } else {
103 19
                    $classMetadata->setDiscriminator($annot->field, $annot->map, $annot->groups);
104
                }
105 35
            } elseif ($annot instanceof XmlDiscriminator) {
106 3
                $classMetadata->xmlDiscriminatorAttribute = (bool) $annot->attribute;
107 3
                $classMetadata->xmlDiscriminatorCData = (bool) $annot->cdata;
108 19
            } elseif ($annot instanceof VirtualProperty) {
109 4
                $virtualPropertyMetadata = new ExpressionPropertyMetadata($name, $annot->name, $annot->exp);
110 4
                $propertiesMetadata[] = $virtualPropertyMetadata;
111 4
                $propertiesAnnotations[] = $annot->options;
112 4
            }
113 234
        }
114
115 234
        foreach ($class->getMethods() as $method) {
116 182
            if ($method->class !== $name) {
117 16
                continue;
118
            }
119
120 181
            $methodAnnotations = $this->reader->getMethodAnnotations($method);
121
122 181
            foreach ($methodAnnotations as $annot) {
123 41
                if ($annot instanceof PreSerialize) {
124 3
                    $classMetadata->addPreSerializeMethod(new MethodMetadata($name, $method->name));
125 3
                    continue 2;
126 41
                } elseif ($annot instanceof PostDeserialize) {
127 6
                    $classMetadata->addPostDeserializeMethod(new MethodMetadata($name, $method->name));
128 6
                    continue 2;
129 38
                } elseif ($annot instanceof PostSerialize) {
130 3
                    $classMetadata->addPostSerializeMethod(new MethodMetadata($name, $method->name));
131 3
                    continue 2;
132 35
                } elseif ($annot instanceof VirtualProperty) {
133 31
                    $virtualPropertyMetadata = new VirtualPropertyMetadata($name, $method->name);
134 31
                    $propertiesMetadata[] = $virtualPropertyMetadata;
135 31
                    $propertiesAnnotations[] = $methodAnnotations;
136 31
                    continue 2;
137 7
                } elseif ($annot instanceof HandlerCallback) {
138 4
                    $classMetadata->addHandlerCallback(GraphNavigator::parseDirection($annot->direction), $annot->format, $method->name);
139 4
                    continue 2;
140
                }
141 153
            }
142 234
        }
143
144 234
        if ( ! $excludeAll) {
145 234
            foreach ($class->getProperties() as $property) {
146 220
                if ($property->class !== $name || (isset($property->info) && $property->info['class'] !== $name)) {
0 ignored issues
show
Bug introduced by
The property info does not seem to exist in ReflectionProperty.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
147 18
                    continue;
148
                }
149 219
                $propertiesMetadata[] = new PropertyMetadata($name, $property->getName());
150 219
                $propertiesAnnotations[] = $this->reader->getPropertyAnnotations($property);
151 234
            }
152
153 234
            foreach ($propertiesMetadata as $propertyKey => $propertyMetadata) {
154 227
                $isExclude = false;
155 227
                $isExpose = $propertyMetadata instanceof VirtualPropertyMetadata;
156 227
                $propertyMetadata->readOnly = $propertyMetadata->readOnly || $readOnlyClass;
157 227
                $accessType = $classAccessType;
158 227
                $accessor = array(null, null);
159
160 227
                $propertyAnnotations = $propertiesAnnotations[$propertyKey];
161
162 227
                foreach ($propertyAnnotations as $annot) {
163 220
                    if ($annot instanceof Since) {
164 7
                        $propertyMetadata->sinceVersion = $annot->version;
165 220
                    } elseif ($annot instanceof Until) {
166 7
                        $propertyMetadata->untilVersion = $annot->version;
167 220
                    } elseif ($annot instanceof SerializedName) {
168 76
                        $propertyMetadata->serializedName = $annot->name;
169 220
                    } elseif ($annot instanceof Expose) {
170 33
                        $isExpose = true;
171 33
                        if (null !== $annot->if) {
172 33
                            $propertyMetadata->excludeIf = "!(" . $annot->if . ")";
173 33
                        }
174 220
                    } elseif ($annot instanceof Exclude) {
175 31
                        if (null !== $annot->if) {
176 21
                            $propertyMetadata->excludeIf = $annot->if;
177 21
                        } else {
178 16
                            $isExclude = true;
179
                        }
180 220
                    } elseif ($annot instanceof Type) {
181 184
                        $propertyMetadata->setType($annot->name);
182 220
                    } elseif ($annot instanceof XmlElement) {
183 15
                        $propertyMetadata->xmlAttribute = false;
184 15
                        $propertyMetadata->xmlElementCData = $annot->cdata;
185 15
                        $propertyMetadata->xmlNamespace = $annot->namespace;
186 140
                    } elseif ($annot instanceof XmlList) {
187 39
                        $propertyMetadata->xmlCollection = true;
188 39
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
189 39
                        $propertyMetadata->xmlEntryName = $annot->entry;
190 39
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
191 39
                        $propertyMetadata->xmlCollectionSkipWhenEmpty = $annot->skipWhenEmpty;
192 140
                    } elseif ($annot instanceof XmlMap) {
193 23
                        $propertyMetadata->xmlCollection = true;
194 23
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
195 23
                        $propertyMetadata->xmlEntryName = $annot->entry;
196 23
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
197 23
                        $propertyMetadata->xmlKeyAttribute = $annot->keyAttribute;
198 135
                    } elseif ($annot instanceof XmlKeyValuePairs) {
199 7
                        $propertyMetadata->xmlKeyValuePairs = true;
200 127
                    } elseif ($annot instanceof XmlAttribute) {
201 40
                        $propertyMetadata->xmlAttribute = true;
202 40
                        $propertyMetadata->xmlNamespace = $annot->namespace;
203 120
                    } elseif ($annot instanceof XmlValue) {
204 26
                        $propertyMetadata->xmlValue = true;
205 26
                        $propertyMetadata->xmlElementCData = $annot->cdata;
206 114
                    } elseif ($annot instanceof XmlElement) {
207
                        $propertyMetadata->xmlElementCData = $annot->cdata;
208 94
                    } elseif ($annot instanceof AccessType) {
209 3
                        $accessType = $annot->type;
210 94
                    } elseif ($annot instanceof ReadOnly) {
211 10
                        $propertyMetadata->readOnly = $annot->readOnly;
212 94
                    } elseif ($annot instanceof Accessor) {
213 11
                        $accessor = array($annot->getter, $annot->setter);
214 91
                    } elseif ($annot instanceof Groups) {
215 37
                        $propertyMetadata->groups = $annot->groups;
216 37
                        foreach ((array) $propertyMetadata->groups as $groupName) {
217 37
                            if (false !== strpos($groupName, ',')) {
218 3
                                throw new InvalidArgumentException(sprintf(
219 3
                                    'Invalid group name "%s" on "%s", did you mean to create multiple groups?',
220 3
                                    implode(', ', $propertyMetadata->groups),
221 3
                                    $propertyMetadata->class.'->'.$propertyMetadata->name
222 3
                                ));
223
                            }
224 34
                        }
225 77
                    } elseif ($annot instanceof Inline) {
226 6
                        $propertyMetadata->inline = true;
227 59
                    } elseif ($annot instanceof XmlAttributeMap) {
228 4
                        $propertyMetadata->xmlAttributeMap = true;
229 53
                    } elseif ($annot instanceof MaxDepth) {
230 6
                        $propertyMetadata->maxDepth = $annot->depth;
231 6
                    }
232 224
                }
233
234
235 224
                if ((ExclusionPolicy::NONE === $exclusionPolicy && ! $isExclude)
236 224
                    || (ExclusionPolicy::ALL === $exclusionPolicy && $isExpose)) {
237 224
                    $propertyMetadata->setAccessor($accessType, $accessor[0], $accessor[1]);
238 224
                    $classMetadata->addPropertyMetadata($propertyMetadata);
239 224
                }
240 231
            }
241 231
        }
242
243 231
        return $classMetadata;
244
    }
245
}
246