Completed
Push — master ( 454f7d...fc2d7f )
by Eric
02:39
created

XmlClassMetadataLoader   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 140
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 21
lcom 1
cbo 1
dl 0
loc 140
rs 10
c 1
b 1
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A loadFile() 0 17 3
C loadProperty() 0 30 7
B loadDocument() 0 35 5
B validateDocument() 0 23 5
A setState() 0 6 1
1
<?php
2
3
/*
4
 * This file is part of the Ivory Serializer package.
5
 *
6
 * (c) Eric GELOEN <[email protected]>
7
 *
8
 * For the full copyright and license information, please read the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Ivory\Serializer\Mapping\Loader;
13
14
/**
15
 * @author GeLo <[email protected]>
16
 */
17
class XmlClassMetadataLoader extends AbstractFileClassMetadataLoader
18
{
19
    /**
20
     * {@inheritdoc}
21
     */
22
    protected function loadFile($file)
23
    {
24
        $data = [];
25
        $xml = simplexml_import_dom($this->loadDocument($file));
26
27
        foreach ($xml->class as $class) {
28
            $properties = [];
29
30
            foreach ($class->property as $property) {
31
                $properties[(string) $property['name']] = $this->loadProperty($property);
32
            }
33
34
            $data[(string) $class['name']] = ['properties' => $properties];
35
        }
36
37
        return $data;
38
    }
39
40
    /**
41
     * @param \SimpleXMLElement $element
42
     *
43
     * @return mixed[]
44
     */
45
    private function loadProperty(\SimpleXMLElement $element)
46
    {
47
        $property = [];
48
49
        if (isset($element['alias'])) {
50
            $property['alias'] = (string) $element['alias'];
51
        }
52
53
        if (isset($element['type'])) {
54
            $property['type'] = (string) $element['type'];
55
        }
56
57
        if (isset($element['since'])) {
58
            $property['since'] = (string) $element['since'];
59
        }
60
61
        if (isset($element['until'])) {
62
            $property['until'] = (string) $element['until'];
63
        }
64
65
        if (isset($element['max-depth'])) {
66
            $property['max_depth'] = (string) $element['max-depth'];
67
        }
68
69
        foreach ($element->group as $group) {
70
            $property['groups'][] = (string) $group;
71
        }
72
73
        return $property;
74
    }
75
76
    /**
77
     * @param string $file
78
     *
79
     * @return \DOMDocument
80
     */
81
    private function loadDocument($file)
82
    {
83
        $data = trim(file_get_contents($file));
84
85
        if (empty($data)) {
86
            throw new \InvalidArgumentException();
87
        }
88
89
        $internalErrors = libxml_use_internal_errors();
90
        $disableEntities = libxml_disable_entity_loader();
91
92
        $this->setState(true, true);
93
94
        $document = new \DOMDocument();
95
        $document->validateOnParse = true;
96
97
        if (!$document->loadXML($data, LIBXML_NONET | LIBXML_COMPACT)) {
98
            $this->setState($internalErrors, $disableEntities);
99
100
            throw new \InvalidArgumentException();
101
        }
102
103
        $document->normalizeDocument();
104
105
        foreach ($document->childNodes as $child) {
106
            if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) {
107
                throw new \InvalidArgumentException('The document type is not allowed.');
108
            }
109
        }
110
111
        $this->validateDocument($document, $internalErrors, $disableEntities);
112
        $this->setState($internalErrors, $disableEntities);
113
114
        return $document;
115
    }
116
117
    /**
118
     * @param \DOMDocument $document
119
     * @param bool         $internalErrors
120
     * @param bool         $disableEntities
121
     */
122
    private function validateDocument(\DOMDocument $document, $internalErrors, $disableEntities)
123
    {
124
        if (@$document->schemaValidateSource(file_get_contents(__DIR__.'/../Resource/mapping.xsd'))) {
125
            return;
126
        }
127
128
        $errors = [];
129
130
        foreach (libxml_get_errors() as $error) {
131
            $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)',
132
                LIBXML_ERR_WARNING === $error->level ? 'WARNING' : 'ERROR',
133
                $error->code,
134
                trim($error->message),
135
                $error->file ?: 'n/a',
136
                $error->line,
137
                $error->column
138
            );
139
        }
140
141
        $this->setState($internalErrors, $disableEntities);
142
143
        throw new \InvalidArgumentException(implode(PHP_EOL, $errors));
144
    }
145
146
    /**
147
     * @param bool $internalErrors
148
     * @param bool $disableEntities
149
     */
150
    private function setState($internalErrors, $disableEntities)
151
    {
152
        libxml_use_internal_errors($internalErrors);
153
        libxml_disable_entity_loader($disableEntities);
154
        libxml_clear_errors();
155
    }
156
}
157