Completed
Push — master ( 3674b7...0c820c )
by Asmir
03:12
created

XmlDriver::getExtension()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
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\GraphNavigator;
22
use JMS\Serializer\Exception\RuntimeException;
23
use JMS\Serializer\Exception\XmlErrorException;
24
use JMS\Serializer\Annotation\ExclusionPolicy;
25
use JMS\Serializer\Metadata\PropertyMetadata;
26
use JMS\Serializer\Metadata\VirtualPropertyMetadata;
27
use Metadata\MethodMetadata;
28
use JMS\Serializer\Metadata\ClassMetadata;
29
use Metadata\Driver\AbstractFileDriver;
30
31
class XmlDriver extends AbstractFileDriver
32
{
33 21
    protected function loadMetadataFromFile(\ReflectionClass $class, $path)
34
    {
35 21
        $previous = libxml_use_internal_errors(true);
36 21
        libxml_clear_errors();
37
38 21
        $elem = simplexml_load_file($path);
39 21
        libxml_use_internal_errors($previous);
40
41 21
        if (false === $elem) {
42 1
            throw new XmlErrorException(libxml_get_last_error());
43
        }
44
45 20
        $metadata = new ClassMetadata($name = $class->name);
46 20
        if ( ! $elems = $elem->xpath("./class[@name = '".$name."']")) {
47
            throw new RuntimeException(sprintf('Could not find class %s inside XML element.', $name));
48
        }
49 20
        $elem = reset($elems);
50
51 20
        $metadata->fileResources[] = $path;
52 20
        $metadata->fileResources[] = $class->getFileName();
53 20
        $exclusionPolicy = strtoupper($elem->attributes()->{'exclusion-policy'}) ?: 'NONE';
54 20
        $excludeAll = null !== ($exclude = $elem->attributes()->exclude) ? 'true' === strtolower($exclude) : false;
55 20
        $classAccessType = (string) ($elem->attributes()->{'access-type'} ?: PropertyMetadata::ACCESS_TYPE_PROPERTY);
56
57 20
        $propertiesMetadata = array();
58 20
        $propertiesNodes = array();
59
60 20
        if (null !== $accessorOrder = $elem->attributes()->{'accessor-order'}) {
61
            $metadata->setAccessorOrder((string) $accessorOrder, preg_split('/\s*,\s*/', (string) $elem->attributes()->{'custom-accessor-order'}));
62
        }
63
64 20
        if (null !== $xmlRootName = $elem->attributes()->{'xml-root-name'}) {
65 7
            $metadata->xmlRootName = (string) $xmlRootName;
66 7
        }
67
68 20
        if (null !== $xmlRootNamespace = $elem->attributes()->{'xml-root-namespace'}) {
69 1
            $metadata->xmlRootNamespace = (string) $xmlRootNamespace;
70 1
        }
71
72 20
        $readOnlyClass = 'true' === strtolower($elem->attributes()->{'read-only'});
73
74 20
        $discriminatorFieldName = (string) $elem->attributes()->{'discriminator-field-name'};
75 20
        $discriminatorMap = array();
76 20
        foreach ($elem->xpath('./discriminator-class') as $entry) {
77 3
            if ( ! isset($entry->attributes()->value)) {
78
                throw new RuntimeException('Each discriminator-class element must have a "value" attribute.');
79
            }
80
81 3
            $discriminatorMap[(string) $entry->attributes()->value] = (string) $entry;
82 20
        }
83
84 20
        if ('true' === (string) $elem->attributes()->{'discriminator-disabled'}) {
85
            $metadata->discriminatorDisabled = true;
86 20
        } elseif ( ! empty($discriminatorFieldName) || ! empty($discriminatorMap)) {
87
88 3
            $discriminatorGroups = array();
89 3
            foreach ($elem->xpath('./discriminator-groups/group') as $entry) {
90 1
                $discriminatorGroups[] = (string) $entry;
91 3
            }
92 3
            $metadata->setDiscriminator($discriminatorFieldName, $discriminatorMap, $discriminatorGroups);
93 3
        }
94
95 20
        foreach ($elem->xpath('./xml-namespace') as $xmlNamespace) {
96 3
            if ( ! isset($xmlNamespace->attributes()->uri)) {
97
                throw new RuntimeException('The prefix attribute must be set for all xml-namespace elements.');
98
            }
99
100 3
            if (isset($xmlNamespace->attributes()->prefix)) {
101 3
                $prefix = (string) $xmlNamespace->attributes()->prefix;
102 3
            } else {
103 2
                $prefix = null;
104
            }
105
106 3
            $metadata->registerNamespace((string) $xmlNamespace->attributes()->uri, $prefix);
107 20
        }
108
109 20
        foreach ($elem->xpath('./xml-discriminator') as $xmlDiscriminator) {
110 1
            if (isset($xmlDiscriminator->attributes()->attribute)) {
111 1
                $metadata->xmlDiscriminatorAttribute = (string) $xmlDiscriminator->attributes()->attribute === 'true';
112 1
            }
113 1
            if (isset($xmlDiscriminator->attributes()->cdata)) {
114 1
                $metadata->xmlDiscriminatorCData = (string) $xmlDiscriminator->attributes()->cdata === 'true';
115 1
            }
116 20
        }
117
118 20
        foreach ($elem->xpath('./virtual-property') as $method) {
119 2
            if ( ! isset($method->attributes()->method)) {
120
                throw new RuntimeException('The method attribute must be set for all virtual-property elements.');
121
            }
122
123 2
            $virtualPropertyMetadata = new VirtualPropertyMetadata($name, (string) $method->attributes()->method);
124
125 2
            $propertiesMetadata[] = $virtualPropertyMetadata;
126 2
            $propertiesNodes[] = $method;
127 20
        }
128
129 20
        if ( ! $excludeAll) {
130
131 20
            foreach ($class->getProperties() as $property) {
132 18
                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...
133 2
                    continue;
134
                }
135
136 17
                $propertiesMetadata[] = new PropertyMetadata($name, $pName = $property->getName());
137 17
                $pElems = $elem->xpath("./property[@name = '".$pName."']");
138
139 17
                $propertiesNodes[] = $pElems ? reset($pElems) : null;
140 20
            }
141
142 20
            foreach ($propertiesMetadata as $propertyKey => $pMetadata) {
143
144 18
                $isExclude = false;
145 18
                $isExpose = $pMetadata instanceof VirtualPropertyMetadata;
146
147 18
                $pElem = $propertiesNodes[$propertyKey];
148 18
                if ( ! empty($pElem)) {
149
150 18
                    if (null !== $exclude = $pElem->attributes()->exclude) {
151 1
                        $isExclude = 'true' === strtolower($exclude);
152 1
                    }
153
154 18
                    if (null !== $expose = $pElem->attributes()->expose) {
155 2
                        $isExpose = 'true' === strtolower($expose);
156 2
                    }
157
158 18
                    if (null !== $excludeIf = $pElem->attributes()->{'exclude-if'}) {
159 1
                        $pMetadata->excludeIf =$excludeIf;
160 1
                    }
161
162 18
                    if (null !== $excludeIf = $pElem->attributes()->{'expose-if'}) {
163 1
                        $pMetadata->excludeIf = "!(" . $excludeIf .")";
164 1
                        $isExpose = true;
165 1
                    }
166
167 18
                    if (null !== $version = $pElem->attributes()->{'since-version'}) {
168
                        $pMetadata->sinceVersion = (string) $version;
169
                    }
170
171 18
                    if (null !== $version = $pElem->attributes()->{'until-version'}) {
172
                        $pMetadata->untilVersion = (string) $version;
173
                    }
174
175 18
                    if (null !== $serializedName = $pElem->attributes()->{'serialized-name'}) {
176 3
                        $pMetadata->serializedName = (string) $serializedName;
177 3
                    }
178
179 18
                    if (null !== $type = $pElem->attributes()->type) {
180 14
                        $pMetadata->setType((string) $type);
181 18
                    } elseif (isset($pElem->type)) {
182 2
                        $pMetadata->setType((string) $pElem->type);
183 2
                    }
184
185 18
                    if (null !== $groups = $pElem->attributes()->groups) {
186 1
                        $pMetadata->groups = preg_split('/\s*,\s*/', (string) $groups);
187 1
                    }
188
189 18
                    if (isset($pElem->{'xml-list'})) {
190
191 2
                        $pMetadata->xmlCollection = true;
192
193 2
                        $colConfig = $pElem->{'xml-list'};
194 2
                        if (isset($colConfig->attributes()->inline)) {
195 1
                            $pMetadata->xmlCollectionInline = 'true' === (string) $colConfig->attributes()->inline;
196 1
                        }
197
198 2
                        if (isset($colConfig->attributes()->{'entry-name'})) {
199 1
                            $pMetadata->xmlEntryName = (string) $colConfig->attributes()->{'entry-name'};
200 1
                        }
201
                        
202 2
                        if (isset($colConfig->attributes()->{'skip-when-empty'})) {
203 1
                            $pMetadata->xmlCollectionSkipWhenEmpty = 'true' === (string) $colConfig->attributes()->{'skip-when-empty'};
204 1
                        } else {
205 1
                            $pMetadata->xmlCollectionSkipWhenEmpty = true;
206
                        }
207
208 2
                        if (isset($colConfig->attributes()->namespace)) {
209
                            $pMetadata->xmlEntryNamespace = (string) $colConfig->attributes()->namespace;
210
                        }
211 2
                    }
212
213 18
                    if (isset($pElem->{'xml-map'})) {
214
                        $pMetadata->xmlCollection = true;
215
216
                        $colConfig = $pElem->{'xml-map'};
217
                        if (isset($colConfig->attributes()->inline)) {
218
                            $pMetadata->xmlCollectionInline = 'true' === (string) $colConfig->attributes()->inline;
219
                        }
220
221
                        if (isset($colConfig->attributes()->{'entry-name'})) {
222
                            $pMetadata->xmlEntryName = (string) $colConfig->attributes()->{'entry-name'};
223
                        }
224
225
                        if (isset($colConfig->attributes()->namespace)) {
226
                            $pMetadata->xmlEntryNamespace = (string) $colConfig->attributes()->namespace;
227
                        }
228
229
                        if (isset($colConfig->attributes()->{'key-attribute-name'})) {
230
                            $pMetadata->xmlKeyAttribute = (string) $colConfig->attributes()->{'key-attribute-name'};
231
                        }
232
                    }
233
234 18
                    if (isset($pElem->{'xml-element'})) {
235 4
                        $colConfig = $pElem->{'xml-element'};
236 4
                        if (isset($colConfig->attributes()->cdata)) {
237 2
                            $pMetadata->xmlElementCData = 'true' === (string) $colConfig->attributes()->cdata;
238 2
                        }
239
240 4
                        if (isset($colConfig->attributes()->namespace)) {
241 3
                            $pMetadata->xmlNamespace = (string) $colConfig->attributes()->namespace;
242 3
                        }
243 4
                    }
244
245 18
                    if (isset($pElem->attributes()->{'xml-attribute'})) {
246 4
                        $pMetadata->xmlAttribute = 'true' === (string) $pElem->attributes()->{'xml-attribute'};
247 4
                    }
248
249 18
                    if (isset($pElem->attributes()->{'xml-attribute-map'})) {
250
                        $pMetadata->xmlAttributeMap = 'true' === (string) $pElem->attributes()->{'xml-attribute-map'};
251
                    }
252
253 18
                    if (isset($pElem->attributes()->{'xml-value'})) {
254 2
                        $pMetadata->xmlValue = 'true' === (string) $pElem->attributes()->{'xml-value'};
255 2
                    }
256
257 18
                    if (isset($pElem->attributes()->{'xml-key-value-pairs'})) {
258 1
                        $pMetadata->xmlKeyValuePairs = 'true' === (string) $pElem->attributes()->{'xml-key-value-pairs'};
259 1
                    }
260
261 18
                    if (isset($pElem->attributes()->{'max-depth'})) {
262 1
                        $pMetadata->maxDepth = (int) $pElem->attributes()->{'max-depth'};
263 1
                    }
264
265
                    //we need read-only before setter and getter set, because that method depends on flag being set
266 18
                    if (null !== $readOnly = $pElem->attributes()->{'read-only'}) {
267 1
                        $pMetadata->readOnly = 'true' === strtolower($readOnly);
268 1
                    } else {
269 17
                        $pMetadata->readOnly = $pMetadata->readOnly || $readOnlyClass;
270
                    }
271
272 18
                    $getter = $pElem->attributes()->{'accessor-getter'};
273 18
                    $setter = $pElem->attributes()->{'accessor-setter'};
274 18
                    $pMetadata->setAccessor(
275 18
                        (string) ($pElem->attributes()->{'access-type'} ?: $classAccessType),
276 18
                        $getter ? (string) $getter : null,
277 18
                        $setter ? (string) $setter : null
278 18
                    );
279
280 18
                    if (null !== $inline = $pElem->attributes()->inline) {
281
                        $pMetadata->inline = 'true' === strtolower($inline);
282
                    }
283
284 18
                }
285
286 18
                if ((ExclusionPolicy::NONE === (string) $exclusionPolicy && ! $isExclude)
287 18
                    || (ExclusionPolicy::ALL === (string) $exclusionPolicy && $isExpose)) {
288
289 18
                    $metadata->addPropertyMetadata($pMetadata);
290 18
                }
291 20
            }
292 20
        }
293
294 20
        foreach ($elem->xpath('./callback-method') as $method) {
295 1
            if ( ! isset($method->attributes()->type)) {
296
                throw new RuntimeException('The type attribute must be set for all callback-method elements.');
297
            }
298 1
            if ( ! isset($method->attributes()->name)) {
299
                throw new RuntimeException('The name attribute must be set for all callback-method elements.');
300
            }
301
302 1
            switch ((string) $method->attributes()->type) {
303 1
                case 'pre-serialize':
304
                    $metadata->addPreSerializeMethod(new MethodMetadata($name, (string) $method->attributes()->name));
305
                    break;
306
307 1
                case 'post-serialize':
308
                    $metadata->addPostSerializeMethod(new MethodMetadata($name, (string) $method->attributes()->name));
309
                    break;
310
311 1
                case 'post-deserialize':
312
                    $metadata->addPostDeserializeMethod(new MethodMetadata($name, (string) $method->attributes()->name));
313
                    break;
314
315 1
                case 'handler':
316 1
                    if ( ! isset($method->attributes()->format)) {
317
                        throw new RuntimeException('The format attribute must be set for "handler" callback methods.');
318
                    }
319 1
                    if ( ! isset($method->attributes()->direction)) {
320
                        throw new RuntimeException('The direction attribute must be set for "handler" callback methods.');
321
                    }
322
323 1
                    $direction = GraphNavigator::parseDirection((string) $method->attributes()->direction);
324 1
                    $format = (string) $method->attributes()->format;
325 1
                    $metadata->addHandlerCallback($direction, $format, (string) $method->attributes()->name);
326
327 1
                    break;
328
329
                default:
330
                    throw new RuntimeException(sprintf('The type "%s" is not supported.', $method->attributes()->name));
331
            }
332 20
        }
333
334 20
        return $metadata;
335
    }
336
337 20
    protected function getExtension()
338
    {
339 20
        return 'xml';
340
    }
341
}
342