Completed
Pull Request — master (#948)
by Asmir
02:53
created

AnnotationDriver::__construct()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 1
nop 3
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * Copyright 2016 Johannes M. Schmitt <[email protected]>
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 *     http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
namespace JMS\Serializer\Metadata\Driver;
22
23
use Doctrine\Common\Annotations\Reader;
24
use JMS\Serializer\Annotation\Accessor;
25
use JMS\Serializer\Annotation\AccessorOrder;
26
use JMS\Serializer\Annotation\AccessType;
27
use JMS\Serializer\Annotation\Discriminator;
28
use JMS\Serializer\Annotation\Exclude;
29
use JMS\Serializer\Annotation\ExclusionPolicy;
30
use JMS\Serializer\Annotation\Expose;
31
use JMS\Serializer\Annotation\Groups;
32
use JMS\Serializer\Annotation\Inline;
33
use JMS\Serializer\Annotation\MaxDepth;
34
use JMS\Serializer\Annotation\PostDeserialize;
35
use JMS\Serializer\Annotation\PostSerialize;
36
use JMS\Serializer\Annotation\PreSerialize;
37
use JMS\Serializer\Annotation\ReadOnly;
38
use JMS\Serializer\Annotation\SerializedName;
39
use JMS\Serializer\Annotation\Since;
40
use JMS\Serializer\Annotation\SkipWhenEmpty;
41
use JMS\Serializer\Annotation\Type;
42
use JMS\Serializer\Annotation\Until;
43
use JMS\Serializer\Annotation\VirtualProperty;
44
use JMS\Serializer\Annotation\XmlAttribute;
45
use JMS\Serializer\Annotation\XmlAttributeMap;
46
use JMS\Serializer\Annotation\XmlDiscriminator;
47
use JMS\Serializer\Annotation\XmlElement;
48
use JMS\Serializer\Annotation\XmlKeyValuePairs;
49
use JMS\Serializer\Annotation\XmlList;
50
use JMS\Serializer\Annotation\XmlMap;
51
use JMS\Serializer\Annotation\XmlNamespace;
52
use JMS\Serializer\Annotation\XmlRoot;
53
use JMS\Serializer\Annotation\XmlValue;
54
use JMS\Serializer\Exception\InvalidMetadataException;
55
use JMS\Serializer\Metadata\ClassMetadata;
56
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
57
use JMS\Serializer\Metadata\PropertyMetadata;
58
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
59
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
60
use JMS\Serializer\Type\Parser;
61
use JMS\Serializer\Type\ParserInterface;
62
use Metadata\Driver\DriverInterface;
63
use Metadata\MethodMetadata;
64
65
class AnnotationDriver implements DriverInterface
66
{
67
    private $reader;
68
    private $typeParser;
69
    /**
70
     * @var PropertyNamingStrategyInterface
71
     */
72
    private $namingStrategy;
73
74 364
    public function __construct(Reader $reader, PropertyNamingStrategyInterface $namingStrategy, ParserInterface $typeParser = null)
75
    {
76 364
        $this->reader = $reader;
77 364
        $this->typeParser = $typeParser ?: new Parser();
78 364
        $this->namingStrategy = $namingStrategy;
79 364
    }
80
81 233
    public function loadMetadataForClass(\ReflectionClass $class): ?\Metadata\ClassMetadata
82
    {
83 233
        $classMetadata = new ClassMetadata($name = $class->name);
84 233
        $classMetadata->fileResources[] = $class->getFilename();
85
86 233
        $propertiesMetadata = [];
87 233
        $propertiesAnnotations = [];
88
89 233
        $exclusionPolicy = 'NONE';
90 233
        $excludeAll = false;
91 233
        $classAccessType = PropertyMetadata::ACCESS_TYPE_PROPERTY;
92 233
        $readOnlyClass = false;
93 233
        foreach ($this->reader->getClassAnnotations($class) as $annot) {
94 125
            if ($annot instanceof ExclusionPolicy) {
95 25
                $exclusionPolicy = $annot->policy;
96 124
            } elseif ($annot instanceof XmlRoot) {
97 57
                $classMetadata->xmlRootName = $annot->name;
98 57
                $classMetadata->xmlRootNamespace = $annot->namespace;
99 57
                $classMetadata->xmlRootPrefix = $annot->prefix;
100 98
            } elseif ($annot instanceof XmlNamespace) {
101 25
                $classMetadata->registerNamespace($annot->uri, $annot->prefix);
102 77
            } elseif ($annot instanceof Exclude) {
103
                $excludeAll = true;
104 77
            } elseif ($annot instanceof AccessType) {
105 4
                $classAccessType = $annot->type;
106 75
            } elseif ($annot instanceof ReadOnly) {
107 4
                $readOnlyClass = true;
108 71
            } elseif ($annot instanceof AccessorOrder) {
109 32
                $classMetadata->setAccessorOrder($annot->order, $annot->custom);
110 39
            } elseif ($annot instanceof Discriminator) {
111 19
                if ($annot->disabled) {
112
                    $classMetadata->discriminatorDisabled = true;
113
                } else {
114 19
                    $classMetadata->setDiscriminator($annot->field, $annot->map, $annot->groups);
115
                }
116 27
            } elseif ($annot instanceof XmlDiscriminator) {
117 7
                $classMetadata->xmlDiscriminatorAttribute = (bool)$annot->attribute;
118 7
                $classMetadata->xmlDiscriminatorCData = (bool)$annot->cdata;
119 7
                $classMetadata->xmlDiscriminatorNamespace = $annot->namespace ? (string)$annot->namespace : null;
120 20
            } elseif ($annot instanceof VirtualProperty) {
121 6
                $virtualPropertyMetadata = new ExpressionPropertyMetadata($name, $annot->name, $annot->exp);
122 6
                $propertiesMetadata[] = $virtualPropertyMetadata;
123 125
                $propertiesAnnotations[] = $annot->options;
124
            }
125
        }
126
127 233
        foreach ($class->getMethods() as $method) {
128 180
            if ($method->class !== $name) {
129 13
                continue;
130
            }
131
132 179
            $methodAnnotations = $this->reader->getMethodAnnotations($method);
133
134 179
            foreach ($methodAnnotations as $annot) {
135 37
                if ($annot instanceof PreSerialize) {
136 2
                    $classMetadata->addPreSerializeMethod(new MethodMetadata($name, $method->name));
137 2
                    continue 2;
138 37
                } elseif ($annot instanceof PostDeserialize) {
139 4
                    $classMetadata->addPostDeserializeMethod(new MethodMetadata($name, $method->name));
140 4
                    continue 2;
141 35
                } elseif ($annot instanceof PostSerialize) {
142 2
                    $classMetadata->addPostSerializeMethod(new MethodMetadata($name, $method->name));
143 2
                    continue 2;
144 33
                } elseif ($annot instanceof VirtualProperty) {
145 33
                    $virtualPropertyMetadata = new VirtualPropertyMetadata($name, $method->name);
146 33
                    $propertiesMetadata[] = $virtualPropertyMetadata;
147 33
                    $propertiesAnnotations[] = $methodAnnotations;
148 179
                    continue 2;
149
                }
150
            }
151
        }
152
153 233
        if (!$excludeAll) {
154 233
            foreach ($class->getProperties() as $property) {
155 212
                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 on ReflectionProperty.
Loading history...
156 15
                    continue;
157
                }
158 211
                $propertiesMetadata[] = new PropertyMetadata($name, $property->getName());
159 211
                $propertiesAnnotations[] = $this->reader->getPropertyAnnotations($property);
160
            }
161
162 233
            foreach ($propertiesMetadata as $propertyKey => $propertyMetadata) {
163 219
                $isExclude = false;
164 219
                $isExpose = $propertyMetadata instanceof VirtualPropertyMetadata
165 219
                    || $propertyMetadata instanceof ExpressionPropertyMetadata;
166 219
                $propertyMetadata->readOnly = $propertyMetadata->readOnly || $readOnlyClass;
167 219
                $accessType = $classAccessType;
168 219
                $accessor = [null, null];
169
170 219
                $propertyAnnotations = $propertiesAnnotations[$propertyKey];
171
172 219
                foreach ($propertyAnnotations as $annot) {
173 216
                    if ($annot instanceof Since) {
174 7
                        $propertyMetadata->sinceVersion = $annot->version;
175 216
                    } elseif ($annot instanceof Until) {
176 7
                        $propertyMetadata->untilVersion = $annot->version;
177 216
                    } elseif ($annot instanceof SerializedName) {
178 75
                        $propertyMetadata->serializedName = $annot->name;
179 215
                    } elseif ($annot instanceof SkipWhenEmpty) {
180 6
                        $propertyMetadata->skipWhenEmpty = true;
181 214
                    } elseif ($annot instanceof Expose) {
182 26
                        $isExpose = true;
183 26
                        if (null !== $annot->if) {
184 26
                            $propertyMetadata->excludeIf = "!(" . $annot->if . ")";
185
                        }
186 214
                    } elseif ($annot instanceof Exclude) {
187 33
                        if (null !== $annot->if) {
188 15
                            $propertyMetadata->excludeIf = $annot->if;
189
                        } else {
190 33
                            $isExclude = true;
191
                        }
192 212
                    } elseif ($annot instanceof Type) {
193 176
                        $propertyMetadata->setType($this->typeParser->parse($annot->name));
194 143
                    } elseif ($annot instanceof XmlElement) {
195 20
                        $propertyMetadata->xmlAttribute = false;
196 20
                        $propertyMetadata->xmlElementCData = $annot->cdata;
197 20
                        $propertyMetadata->xmlNamespace = $annot->namespace;
198 137
                    } elseif ($annot instanceof XmlList) {
199 39
                        $propertyMetadata->xmlCollection = true;
200 39
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
201 39
                        $propertyMetadata->xmlEntryName = $annot->entry;
202 39
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
203 39
                        $propertyMetadata->xmlCollectionSkipWhenEmpty = $annot->skipWhenEmpty;
204 131
                    } elseif ($annot instanceof XmlMap) {
205 19
                        $propertyMetadata->xmlCollection = true;
206 19
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
207 19
                        $propertyMetadata->xmlEntryName = $annot->entry;
208 19
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
209 19
                        $propertyMetadata->xmlKeyAttribute = $annot->keyAttribute;
210 125
                    } elseif ($annot instanceof XmlKeyValuePairs) {
211 7
                        $propertyMetadata->xmlKeyValuePairs = true;
212 118
                    } elseif ($annot instanceof XmlAttribute) {
213 39
                        $propertyMetadata->xmlAttribute = true;
214 39
                        $propertyMetadata->xmlNamespace = $annot->namespace;
215 112
                    } elseif ($annot instanceof XmlValue) {
216 23
                        $propertyMetadata->xmlValue = true;
217 23
                        $propertyMetadata->xmlElementCData = $annot->cdata;
218 95
                    } elseif ($annot instanceof XmlElement) {
219
                        $propertyMetadata->xmlElementCData = $annot->cdata;
220 95
                    } elseif ($annot instanceof AccessType) {
221 2
                        $accessType = $annot->type;
222 95
                    } elseif ($annot instanceof ReadOnly) {
223 7
                        $propertyMetadata->readOnly = $annot->readOnly;
224 93
                    } elseif ($annot instanceof Accessor) {
225 8
                        $accessor = [$annot->getter, $annot->setter];
226 85
                    } elseif ($annot instanceof Groups) {
227 37
                        $propertyMetadata->groups = $annot->groups;
228 37
                        foreach ((array)$propertyMetadata->groups as $groupName) {
229 37
                            if (false !== strpos($groupName, ',')) {
230 2
                                throw new InvalidMetadataException(sprintf(
231 2
                                    'Invalid group name "%s" on "%s", did you mean to create multiple groups?',
232 2
                                    implode(', ', $propertyMetadata->groups),
233 37
                                    $propertyMetadata->class . '->' . $propertyMetadata->name
234
                                ));
235
                            }
236
                        }
237 65
                    } elseif ($annot instanceof Inline) {
238 13
                        $propertyMetadata->inline = true;
239 52
                    } elseif ($annot instanceof XmlAttributeMap) {
240 3
                        $propertyMetadata->xmlAttributeMap = true;
241 49
                    } elseif ($annot instanceof MaxDepth) {
242 214
                        $propertyMetadata->maxDepth = $annot->depth;
243
                    }
244
                }
245
246 217
                if ($propertyMetadata->inline) {
247 13
                    $classMetadata->isList = $classMetadata->isList || PropertyMetadata::isCollectionList($propertyMetadata->type);
248 13
                    $classMetadata->isMap = $classMetadata->isMap || PropertyMetadata::isCollectionMap($propertyMetadata->type);
249
250 13
                    if ($classMetadata->isMap && $classMetadata->isList) {
251
                        throw new InvalidMetadataException("Can not have an inline map and and inline map on the same class");
252
                    }
253
                }
254
255 217
                if (!$propertyMetadata->serializedName) {
256 199
                    $propertyMetadata->serializedName = $this->namingStrategy->translateName($propertyMetadata);
257
                }
258
259 217
                foreach ($propertyAnnotations as $annot) {
260 214
                    if ($annot instanceof VirtualProperty && $annot->name !== null) {
261 214
                        $propertyMetadata->name = $annot->name;
262
                    }
263
                }
264
265 217
                if ((ExclusionPolicy::NONE === $exclusionPolicy && !$isExclude)
266 217
                    || (ExclusionPolicy::ALL === $exclusionPolicy && $isExpose)
267
                ) {
268 217
                    $propertyMetadata->setAccessor($accessType, $accessor[0], $accessor[1]);
269 217
                    $classMetadata->addPropertyMetadata($propertyMetadata);
270
                }
271
            }
272
        }
273
274 231
        return $classMetadata;
275
    }
276
}
277