Passed
Branch master (bf85d9)
by Johannes
05:40
created

YamlDriver::__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
/*
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\Parser\AbstractParser;
22
use JMS\Serializer\Annotation\ExclusionPolicy;
23
use JMS\Serializer\Exception\RuntimeException;
24
use JMS\Serializer\Metadata\ClassMetadata;
25
use JMS\Serializer\Metadata\ExpressionPropertyMetadata;
26
use JMS\Serializer\Metadata\PropertyMetadata;
27
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
28
use JMS\Serializer\Naming\PropertyNamingStrategyInterface;
29
use JMS\Serializer\TypeParser;
30
use Metadata\Driver\AbstractFileDriver;
31
use Metadata\Driver\FileLocatorInterface;
32
use Metadata\MethodMetadata;
33
use Symfony\Component\Yaml\Yaml;
34
35
class YamlDriver extends AbstractFileDriver
36
{
37
    private $typeParser;
38
    /**
39
     * @var PropertyNamingStrategyInterface
40
     */
41
    private $namingStrategy;
42
43 27
    public function __construct(FileLocatorInterface $locator, PropertyNamingStrategyInterface $namingStrategy,  AbstractParser $typeParser = null)
44
    {
45 27
        parent::__construct($locator);
46 27
        $this->typeParser = $typeParser ?: new TypeParser();
47 27
        $this->namingStrategy = $namingStrategy;
48 27
    }
49
50 27
    protected function loadMetadataFromFile(\ReflectionClass $class, $file)
51
    {
52 27
        $config = Yaml::parse(file_get_contents($file));
53
54 27
        if (!isset($config[$name = $class->name])) {
55
            throw new RuntimeException(sprintf('Expected metadata for class %s to be defined in %s.', $class->name, $file));
56
        }
57
58 27
        $config = $config[$name];
59 27
        $metadata = new ClassMetadata($name);
60 27
        $metadata->fileResources[] = $file;
61 27
        $metadata->fileResources[] = $class->getFileName();
62 27
        $exclusionPolicy = isset($config['exclusion_policy']) ? strtoupper($config['exclusion_policy']) : 'NONE';
63 27
        $excludeAll = isset($config['exclude']) ? (Boolean)$config['exclude'] : false;
64 27
        $classAccessType = isset($config['access_type']) ? $config['access_type'] : PropertyMetadata::ACCESS_TYPE_PROPERTY;
65 27
        $readOnlyClass = isset($config['read_only']) ? (Boolean)$config['read_only'] : false;
66 27
        $this->addClassProperties($metadata, $config);
67
68 27
        $propertiesMetadata = array();
69 27
        if (array_key_exists('virtual_properties', $config)) {
70 5
            foreach ($config['virtual_properties'] as $methodName => $propertySettings) {
71 5
                if (isset($propertySettings['exp'])) {
72 2
                    $virtualPropertyMetadata = new ExpressionPropertyMetadata($name, $methodName, $propertySettings['exp']);
73 2
                    unset($propertySettings['exp']);
74
75
                } else {
76
77 4
                    if (!$class->hasMethod($methodName)) {
78
                        throw new RuntimeException('The method ' . $methodName . ' not found in class ' . $class->name);
79
                    }
80 4
                    $virtualPropertyMetadata = new VirtualPropertyMetadata($name, $methodName);
81
                }
82
83 5
                $pName = !empty($propertySettings["name"]) ? $propertySettings["name"] : $virtualPropertyMetadata->name;
84
85 5
                $propertiesMetadata[$pName] = $virtualPropertyMetadata;
86 5
                $config['properties'][$pName] = $propertySettings;
87
            }
88
        }
89
90 27
        if (!$excludeAll) {
91 27
            foreach ($class->getProperties() as $property) {
92 23
                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...
93 2
                    continue;
94
                }
95
96 22
                $pName = $property->getName();
97 22
                $propertiesMetadata[$pName] = new PropertyMetadata($name, $pName);
98
            }
99
100 27
            foreach ($propertiesMetadata as $pName => $pMetadata) {
101 24
                $isExclude = false;
102 24
                $isExpose = $pMetadata instanceof VirtualPropertyMetadata
103 23
                    || $pMetadata instanceof ExpressionPropertyMetadata
104 24
                    || (isset($config['properties']) && array_key_exists($pName, $config['properties']));
105
106 24
                if (isset($config['properties'][$pName])) {
107 21
                    $pConfig = $config['properties'][$pName];
108
109 21
                    if (isset($pConfig['exclude'])) {
110 2
                        $isExclude = (Boolean)$pConfig['exclude'];
111
                    }
112
113 21
                    if ($isExclude) {
114 2
                        continue;
115
                    }
116
117 19
                    if (isset($pConfig['expose'])) {
118 3
                        $isExpose = (Boolean)$pConfig['expose'];
119
                    }
120
121 19
                    if (isset($pConfig['skip_when_empty'])) {
122 1
                        $pMetadata->skipWhenEmpty = (Boolean)$pConfig['skip_when_empty'];
123
                    }
124
125 19
                    if (isset($pConfig['since_version'])) {
126
                        $pMetadata->sinceVersion = (string)$pConfig['since_version'];
127
                    }
128
129 19
                    if (isset($pConfig['until_version'])) {
130
                        $pMetadata->untilVersion = (string)$pConfig['until_version'];
131
                    }
132
133 19
                    if (isset($pConfig['exclude_if'])) {
134 1
                        $pMetadata->excludeIf = (string)$pConfig['exclude_if'];
135
                    }
136
137 19
                    if (isset($pConfig['expose_if'])) {
138 1
                        $pMetadata->excludeIf = "!(" . $pConfig['expose_if'] . ")";
139
                    }
140
141 19
                    if (isset($pConfig['serialized_name'])) {
142 4
                        $pMetadata->serializedName = (string)$pConfig['serialized_name'];
143
                    }
144
145 19
                    if (isset($pConfig['type'])) {
146 13
                        $pMetadata->setType($this->typeParser->parse((string)$pConfig['type']));
147
                    }
148
149 19
                    if (isset($pConfig['groups'])) {
150 1
                        $pMetadata->groups = $pConfig['groups'];
151
                    }
152
153 19
                    if (isset($pConfig['xml_list'])) {
154 2
                        $pMetadata->xmlCollection = true;
155
156 2
                        $colConfig = $pConfig['xml_list'];
157 2
                        if (isset($colConfig['inline'])) {
158 2
                            $pMetadata->xmlCollectionInline = (Boolean)$colConfig['inline'];
159
                        }
160
161 2
                        if (isset($colConfig['entry_name'])) {
162 1
                            $pMetadata->xmlEntryName = (string)$colConfig['entry_name'];
163
                        }
164
165 2
                        if (isset($colConfig['skip_when_empty'])) {
166 1
                            $pMetadata->xmlCollectionSkipWhenEmpty = (Boolean)$colConfig['skip_when_empty'];
167
                        } else {
168 2
                            $pMetadata->xmlCollectionSkipWhenEmpty = true;
169
                        }
170
171 2
                        if (isset($colConfig['namespace'])) {
172
                            $pMetadata->xmlEntryNamespace = (string)$colConfig['namespace'];
173
                        }
174
                    }
175
176 19
                    if (isset($pConfig['xml_map'])) {
177
                        $pMetadata->xmlCollection = true;
178
179
                        $colConfig = $pConfig['xml_map'];
180
                        if (isset($colConfig['inline'])) {
181
                            $pMetadata->xmlCollectionInline = (Boolean)$colConfig['inline'];
182
                        }
183
184
                        if (isset($colConfig['entry_name'])) {
185
                            $pMetadata->xmlEntryName = (string)$colConfig['entry_name'];
186
                        }
187
188
                        if (isset($colConfig['namespace'])) {
189
                            $pMetadata->xmlEntryNamespace = (string)$colConfig['namespace'];
190
                        }
191
192
                        if (isset($colConfig['key_attribute_name'])) {
193
                            $pMetadata->xmlKeyAttribute = $colConfig['key_attribute_name'];
194
                        }
195
196
                    }
197
198 19
                    if (isset($pConfig['xml_element'])) {
199 4
                        $colConfig = $pConfig['xml_element'];
200 4
                        if (isset($colConfig['cdata'])) {
201 2
                            $pMetadata->xmlElementCData = (Boolean)$colConfig['cdata'];
202
                        }
203
204 4
                        if (isset($colConfig['namespace'])) {
205 3
                            $pMetadata->xmlNamespace = (string)$colConfig['namespace'];
206
                        }
207
                    }
208
209 19
                    if (isset($pConfig['xml_attribute'])) {
210 4
                        $pMetadata->xmlAttribute = (Boolean)$pConfig['xml_attribute'];
211
                    }
212
213 19
                    if (isset($pConfig['xml_attribute_map'])) {
214
                        $pMetadata->xmlAttributeMap = (Boolean)$pConfig['xml_attribute_map'];
215
                    }
216
217 19
                    if (isset($pConfig['xml_value'])) {
218 2
                        $pMetadata->xmlValue = (Boolean)$pConfig['xml_value'];
219
                    }
220
221 19
                    if (isset($pConfig['xml_key_value_pairs'])) {
222 1
                        $pMetadata->xmlKeyValuePairs = (Boolean)$pConfig['xml_key_value_pairs'];
223
                    }
224
225
                    //we need read_only before setter and getter set, because that method depends on flag being set
226 19
                    if (isset($pConfig['read_only'])) {
227 1
                        $pMetadata->readOnly = (Boolean)$pConfig['read_only'];
228
                    } else {
229 18
                        $pMetadata->readOnly = $pMetadata->readOnly || $readOnlyClass;
230
                    }
231
232 19
                    $pMetadata->setAccessor(
233 19
                        isset($pConfig['access_type']) ? $pConfig['access_type'] : $classAccessType,
234 19
                        isset($pConfig['accessor']['getter']) ? $pConfig['accessor']['getter'] : null,
235 19
                        isset($pConfig['accessor']['setter']) ? $pConfig['accessor']['setter'] : null
236
                    );
237
238 19
                    if (isset($pConfig['inline'])) {
239
                        $pMetadata->inline = (Boolean)$pConfig['inline'];
240
                    }
241
242 19
                    if (isset($pConfig['max_depth'])) {
243 1
                        $pMetadata->maxDepth = (int)$pConfig['max_depth'];
244
                    }
245
                }
246
247 24
                if (!$pMetadata->serializedName) {
248 24
                    $pMetadata->serializedName = $this->namingStrategy->translateName($pMetadata);
249
                }
250
251 24
                if (isset($config['properties'][$pName])) {
252 19
                    $pConfig = $config['properties'][$pName];
253
254 19
                    if (isset($pConfig['name'])) {
255 1
                        $pMetadata->name = (string)$pConfig['name'];
256
                    }
257
                }
258
259 24
                if ((ExclusionPolicy::NONE === $exclusionPolicy && !$isExclude)
260 24
                    || (ExclusionPolicy::ALL === $exclusionPolicy && $isExpose)
261
                ) {
262 24
                    $metadata->addPropertyMetadata($pMetadata);
263
                }
264
            }
265
        }
266
267 27
        if (isset($config['callback_methods'])) {
268
            $cConfig = $config['callback_methods'];
269
270
            if (isset($cConfig['pre_serialize'])) {
271
                $metadata->preSerializeMethods = $this->getCallbackMetadata($class, $cConfig['pre_serialize']);
272
            }
273
            if (isset($cConfig['post_serialize'])) {
274
                $metadata->postSerializeMethods = $this->getCallbackMetadata($class, $cConfig['post_serialize']);
275
            }
276
            if (isset($cConfig['post_deserialize'])) {
277
                $metadata->postDeserializeMethods = $this->getCallbackMetadata($class, $cConfig['post_deserialize']);
278
            }
279
        }
280
281 27
        return $metadata;
282
    }
283
284 27
    protected function getExtension()
285
    {
286 27
        return 'yml';
287
    }
288
289 27
    private function addClassProperties(ClassMetadata $metadata, array $config)
290
    {
291 27
        if (isset($config['custom_accessor_order']) && !isset($config['accessor_order'])) {
292 1
            $config['accessor_order'] = 'custom';
293
        }
294
295 27
        if (isset($config['accessor_order'])) {
296 1
            $metadata->setAccessorOrder($config['accessor_order'], isset($config['custom_accessor_order']) ? $config['custom_accessor_order'] : array());
297
        }
298
299 27
        if (isset($config['xml_root_name'])) {
300 9
            $metadata->xmlRootName = (string)$config['xml_root_name'];
301
        }
302
303 27
        if (isset($config['xml_root_namespace'])) {
304 1
            $metadata->xmlRootNamespace = (string)$config['xml_root_namespace'];
305
        }
306
307 27
        if (array_key_exists('xml_namespaces', $config)) {
308
309 3
            foreach ($config['xml_namespaces'] as $prefix => $uri) {
310 3
                $metadata->registerNamespace($uri, $prefix);
311
            }
312
313
        }
314
315 27
        if (isset($config['discriminator'])) {
316 4
            if (isset($config['discriminator']['disabled']) && true === $config['discriminator']['disabled']) {
317
                $metadata->discriminatorDisabled = true;
318
            } else {
319 4
                if (!isset($config['discriminator']['field_name'])) {
320
                    throw new RuntimeException('The "field_name" attribute must be set for discriminators.');
321
                }
322
323 4
                if (!isset($config['discriminator']['map']) || !\is_array($config['discriminator']['map'])) {
324
                    throw new RuntimeException('The "map" attribute must be set, and be an array for discriminators.');
325
                }
326 4
                $groups = isset($config['discriminator']['groups']) ? $config['discriminator']['groups'] : array();
327 4
                $metadata->setDiscriminator($config['discriminator']['field_name'], $config['discriminator']['map'], $groups);
328
329 4
                if (isset($config['discriminator']['xml_attribute'])) {
330 1
                    $metadata->xmlDiscriminatorAttribute = (bool)$config['discriminator']['xml_attribute'];
331
                }
332 4
                if (isset($config['discriminator']['xml_element'])) {
333 2
                    if (isset($config['discriminator']['xml_element']['cdata'])) {
334 1
                        $metadata->xmlDiscriminatorCData = (bool)$config['discriminator']['xml_element']['cdata'];
335
                    }
336 2
                    if (isset($config['discriminator']['xml_element']['namespace'])) {
337 1
                        $metadata->xmlDiscriminatorNamespace = (string)$config['discriminator']['xml_element']['namespace'];
338
                    }
339
                }
340
341
            }
342
        }
343 27
    }
344
345
    private function getCallbackMetadata(\ReflectionClass $class, $config)
346
    {
347
        if (\is_string($config)) {
348
            $config = array($config);
349
        } elseif (!\is_array($config)) {
350
            throw new RuntimeException(sprintf('callback methods expects a string, or an array of strings that represent method names, but got %s.', json_encode($config['pre_serialize'])));
351
        }
352
353
        $methods = array();
354
        foreach ($config as $name) {
355
            if (!$class->hasMethod($name)) {
356
                throw new RuntimeException(sprintf('The method %s does not exist in class %s.', $name, $class->name));
357
            }
358
359
            $methods[] = new MethodMetadata($class->name, $name);
360
        }
361
362
        return $methods;
363
    }
364
}
365