Completed
Pull Request — master (#884)
by Robert
10:46 queued 04:46
created

AnnotationDriver::findAccessors()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
dl 0
loc 19
c 0
b 0
f 0
ccs 7
cts 7
cp 1
rs 9.4285
cc 3
eloc 11
nc 4
nop 4
crap 3
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 Doctrine\Common\Annotations\Reader;
22
use JMS\Serializer\Accessor\Finder\AccessorFinderInterface;
23
use JMS\Serializer\Annotation\Accessor;
24
use JMS\Serializer\Annotation\AccessorOrder;
25
use JMS\Serializer\Annotation\AccessType;
26
use JMS\Serializer\Annotation\Discriminator;
27
use JMS\Serializer\Annotation\Exclude;
28
use JMS\Serializer\Annotation\ExcludeIf;
29
use JMS\Serializer\Annotation\ExclusionPolicy;
30
use JMS\Serializer\Annotation\Expose;
31
use JMS\Serializer\Annotation\Groups;
32
use JMS\Serializer\Annotation\AccessorFinder;
33
use JMS\Serializer\Annotation\HandlerCallback;
34
use JMS\Serializer\Annotation\Inline;
35
use JMS\Serializer\Annotation\MaxDepth;
36
use JMS\Serializer\Annotation\PostDeserialize;
37
use JMS\Serializer\Annotation\PostSerialize;
38
use JMS\Serializer\Annotation\PreSerialize;
39
use JMS\Serializer\Annotation\ReadOnly;
40
use JMS\Serializer\Annotation\SerializedName;
41
use JMS\Serializer\Annotation\Since;
42
use JMS\Serializer\Annotation\SkipWhenEmpty;
43
use JMS\Serializer\Annotation\Type;
44
use JMS\Serializer\Annotation\Until;
45
use JMS\Serializer\Annotation\VirtualProperty;
46
use JMS\Serializer\Annotation\XmlAttribute;
47
use JMS\Serializer\Annotation\XmlAttributeMap;
48
use JMS\Serializer\Annotation\XmlDiscriminator;
49
use JMS\Serializer\Annotation\XmlElement;
50
use JMS\Serializer\Annotation\XmlKeyValuePairs;
51
use JMS\Serializer\Annotation\XmlList;
52
use JMS\Serializer\Annotation\XmlMap;
53
use JMS\Serializer\Annotation\XmlNamespace;
54
use JMS\Serializer\Annotation\XmlRoot;
55
use JMS\Serializer\Annotation\XmlValue;
56
use JMS\Serializer\Exception\InvalidArgumentException;
57
use JMS\Serializer\Exception\LogicException;
58
use JMS\Serializer\GraphNavigator;
59
use JMS\Serializer\Metadata\ClassMetadata;
60
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
61
use JMS\Serializer\Metadata\PropertyMetadata;
62
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
63
use Metadata\Driver\DriverInterface;
64
use Metadata\MethodMetadata;
65
66
class AnnotationDriver implements DriverInterface
67
{
68
    private $reader;
69
70 444
    public function __construct(Reader $reader)
71
    {
72 444
        $this->reader = $reader;
73 444
    }
74
75 277
    public function loadMetadataForClass(\ReflectionClass $class)
76
    {
77 277
        $classMetadata = new ClassMetadata($name = $class->name);
78 277
        $classMetadata->fileResources[] = $class->getFilename();
79
80 277
        $propertiesMetadata = array();
81 277
        $propertiesAnnotations = array();
82
83 277
        $exclusionPolicy = 'NONE';
84 277
        $excludeAll = false;
85 277
        $classAccessType = PropertyMetadata::ACCESS_TYPE_PROPERTY;
86 277
        $readOnlyClass = false;
87 277
        $accessorFinder = null;
88 277
        foreach ($this->reader->getClassAnnotations($class) as $annot) {
89 159
            if ($annot instanceof ExclusionPolicy) {
90 35
                $exclusionPolicy = $annot->policy;
91 158
            } elseif ($annot instanceof AccessorFinder) {
92 4
                $accessorFinder = $annot->class;
93 4
                $accessorFinder = new $accessorFinder();
94 4
                if (!$accessorFinder instanceof AccessorFinderInterface) {
95 4
                    throw new LogicException('Wrong accessor finder type for class: ' . $class->name);
96
                }
97 154
            } elseif ($annot instanceof XmlRoot) {
98 68
                $classMetadata->xmlRootName = $annot->name;
99 68
                $classMetadata->xmlRootNamespace = $annot->namespace;
100 119
            } elseif ($annot instanceof XmlNamespace) {
101 24
                $classMetadata->registerNamespace($annot->uri, $annot->prefix);
102 97
            } elseif ($annot instanceof Exclude) {
103
                $excludeAll = true;
104 97
            } elseif ($annot instanceof AccessType) {
105 5
                $classAccessType = $annot->type;
106 94
            } elseif ($annot instanceof ReadOnly) {
107 5
                $readOnlyClass = true;
108 89
            } elseif ($annot instanceof AccessorOrder) {
109 46
                $classMetadata->setAccessorOrder($annot->order, $annot->custom);
110 43
            } elseif ($annot instanceof Discriminator) {
111 21
                if ($annot->disabled) {
112
                    $classMetadata->discriminatorDisabled = true;
113
                } else {
114 21
                    $classMetadata->setDiscriminator($annot->field, $annot->map, $annot->groups);
115
                }
116 27
            } elseif ($annot instanceof XmlDiscriminator) {
117 5
                $classMetadata->xmlDiscriminatorAttribute = (bool)$annot->attribute;
118 5
                $classMetadata->xmlDiscriminatorCData = (bool)$annot->cdata;
119 5
                $classMetadata->xmlDiscriminatorNamespace = $annot->namespace ? (string)$annot->namespace : null;
120 22
            } elseif ($annot instanceof VirtualProperty) {
121 8
                $virtualPropertyMetadata = new ExpressionPropertyMetadata($name, $annot->name, $annot->exp);
122 8
                $propertiesMetadata[] = $virtualPropertyMetadata;
123 159
                $propertiesAnnotations[] = $annot->options;
124
            }
125
        }
126
127 277
        foreach ($class->getMethods() as $method) {
128 218
            if ($method->class !== $name) {
129 18
                continue;
130
            }
131
132 217
            $methodAnnotations = $this->reader->getMethodAnnotations($method);
133
134 217
            foreach ($methodAnnotations as $annot) {
135 51
                if ($annot instanceof PreSerialize) {
136 3
                    $classMetadata->addPreSerializeMethod(new MethodMetadata($name, $method->name));
137 3
                    continue 2;
138 51
                } elseif ($annot instanceof PostDeserialize) {
139 6
                    $classMetadata->addPostDeserializeMethod(new MethodMetadata($name, $method->name));
140 6
                    continue 2;
141 48
                } elseif ($annot instanceof PostSerialize) {
142 3
                    $classMetadata->addPostSerializeMethod(new MethodMetadata($name, $method->name));
143 3
                    continue 2;
144 45
                } elseif ($annot instanceof VirtualProperty) {
145 41
                    $virtualPropertyMetadata = new VirtualPropertyMetadata($name, $method->name);
146 41
                    $propertiesMetadata[] = $virtualPropertyMetadata;
147 41
                    $propertiesAnnotations[] = $methodAnnotations;
148 41
                    continue 2;
149 7
                } elseif ($annot instanceof HandlerCallback) {
150 4
                    $classMetadata->addHandlerCallback(GraphNavigator::parseDirection($annot->direction), $annot->format, $method->name);
151 193
                    continue 2;
152
                }
153
            }
154
        }
155
156 277
        if (!$excludeAll) {
157 277
            foreach ($class->getProperties() as $property) {
158 256
                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...
159 20
                    continue;
160
                }
161 255
                $propertiesMetadata[] = new PropertyMetadata($name, $property->getName());
162 255
                $propertiesAnnotations[] = $this->reader->getPropertyAnnotations($property);
163
            }
164
165 277
            foreach ($propertiesMetadata as $propertyKey => $propertyMetadata) {
166 264
                $isExclude = false;
167 264
                $isExpose = $propertyMetadata instanceof VirtualPropertyMetadata
168 264
                    || $propertyMetadata instanceof ExpressionPropertyMetadata;
169 264
                $propertyMetadata->readOnly = $propertyMetadata->readOnly || $readOnlyClass;
170 264
                $accessType = $classAccessType;
171 264
                $accessor = array(null, null);
172
173 264
                $propertyAnnotations = $propertiesAnnotations[$propertyKey];
174
175 264
                foreach ($propertyAnnotations as $annot) {
176 256
                    if ($annot instanceof Since) {
177 7
                        $propertyMetadata->sinceVersion = $annot->version;
178 256
                    } elseif ($annot instanceof Until) {
179 7
                        $propertyMetadata->untilVersion = $annot->version;
180 256
                    } elseif ($annot instanceof SerializedName) {
181 86
                        $propertyMetadata->serializedName = $annot->name;
182 256
                    } elseif ($annot instanceof SkipWhenEmpty) {
183 7
                        $propertyMetadata->skipWhenEmpty = true;
184 255
                    } elseif ($annot instanceof Expose) {
185 37
                        $isExpose = true;
186 37
                        if (null !== $annot->if) {
187 37
                            $propertyMetadata->excludeIf = "!(" . $annot->if . ")";
188
                        }
189 255
                    } elseif ($annot instanceof Exclude) {
190 43
                        if (null !== $annot->if) {
191 21
                            $propertyMetadata->excludeIf = $annot->if;
192
                        } else {
193 43
                            $isExclude = true;
194
                        }
195 253
                    } elseif ($annot instanceof Type) {
196 211
                        $propertyMetadata->setType($annot->name);
197 166
                    } elseif ($annot instanceof XmlElement) {
198 21
                        $propertyMetadata->xmlAttribute = false;
199 21
                        $propertyMetadata->xmlElementCData = $annot->cdata;
200 21
                        $propertyMetadata->xmlNamespace = $annot->namespace;
201 160
                    } elseif ($annot instanceof XmlList) {
202 45
                        $propertyMetadata->xmlCollection = true;
203 45
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
204 45
                        $propertyMetadata->xmlEntryName = $annot->entry;
205 45
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
206 45
                        $propertyMetadata->xmlCollectionSkipWhenEmpty = $annot->skipWhenEmpty;
207 152
                    } elseif ($annot instanceof XmlMap) {
208 23
                        $propertyMetadata->xmlCollection = true;
209 23
                        $propertyMetadata->xmlCollectionInline = $annot->inline;
210 23
                        $propertyMetadata->xmlEntryName = $annot->entry;
211 23
                        $propertyMetadata->xmlEntryNamespace = $annot->namespace;
212 23
                        $propertyMetadata->xmlKeyAttribute = $annot->keyAttribute;
213 144
                    } elseif ($annot instanceof XmlKeyValuePairs) {
214 9
                        $propertyMetadata->xmlKeyValuePairs = true;
215 135
                    } elseif ($annot instanceof XmlAttribute) {
216 44
                        $propertyMetadata->xmlAttribute = true;
217 44
                        $propertyMetadata->xmlNamespace = $annot->namespace;
218 129
                    } elseif ($annot instanceof XmlValue) {
219 28
                        $propertyMetadata->xmlValue = true;
220 28
                        $propertyMetadata->xmlElementCData = $annot->cdata;
221 107
                    } elseif ($annot instanceof XmlElement) {
222
                        $propertyMetadata->xmlElementCData = $annot->cdata;
223 107
                    } elseif ($annot instanceof AccessType) {
224 3
                        $accessType = $annot->type;
225 107
                    } elseif ($annot instanceof ReadOnly) {
226 10
                        $propertyMetadata->readOnly = $annot->readOnly;
227 104
                    } elseif ($annot instanceof Accessor) {
228 11
                        $accessor = array($annot->getter, $annot->setter);
229 93
                    } elseif ($annot instanceof Groups) {
230 43
                        $propertyMetadata->groups = $annot->groups;
231 43
                        foreach ((array)$propertyMetadata->groups as $groupName) {
232 43
                            if (false !== strpos($groupName, ',')) {
233 3
                                throw new InvalidArgumentException(sprintf(
234 3
                                    'Invalid group name "%s" on "%s", did you mean to create multiple groups?',
235 3
                                    implode(', ', $propertyMetadata->groups),
236 43
                                    $propertyMetadata->class . '->' . $propertyMetadata->name
237
                                ));
238
                            }
239
                        }
240 68
                    } elseif ($annot instanceof Inline) {
241 7
                        $propertyMetadata->inline = true;
242 61
                    } elseif ($annot instanceof XmlAttributeMap) {
243 4
                        $propertyMetadata->xmlAttributeMap = true;
244 57
                    } elseif ($annot instanceof MaxDepth) {
245 253
                        $propertyMetadata->maxDepth = $annot->depth;
246
                    }
247
                }
248
249
250 261
                if ((ExclusionPolicy::NONE === $exclusionPolicy && !$isExclude)
251 261
                    || (ExclusionPolicy::ALL === $exclusionPolicy && $isExpose)
252
                ) {
253 261
                    if ($accessorFinder) {
254 4
                        $accessor = $this->findAccessors(
255 4
                            $accessorFinder,
256 4
                            $accessor,
257 4
                            $class,
258 4
                            $propertyMetadata
259
                        );
260
                    }
261 261
                    $propertyMetadata->setAccessor($accessType, $accessor[0], $accessor[1]);
262 261
                    $classMetadata->addPropertyMetadata($propertyMetadata);
263
                }
264
            }
265
        }
266
267 274
        return $classMetadata;
268
    }
269
270
    /**
271
     * @param AccessorFinderInterface $guesser
272
     * @param array $accessor Getter and setter.
273
     * @param \ReflectionClass $class
274
     * @param PropertyMetadata $metadata
275
     *
276
     * @return array
277
     */
278 4
    private function findAccessors(
279
        AccessorFinderInterface $guesser,
280
        array $accessor,
281
        \ReflectionClass $class,
282
        PropertyMetadata $metadata
283
    )
284
    {
285 4
        list($getter, $setter) = $accessor;
286
287 4
        if ($getter === null) {
288 4
            $getter = $guesser->findGetter($class, $metadata->name);
289
        }
290
291 4
        if ($setter === null) {
292 4
            $setter = $guesser->findSetter($class, $metadata->name);
293
        }
294
295 4
        return [$getter, $setter];
296
    }
297
}
298