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