Completed
Push — master ( ed0613...204a50 )
by Martin
01:42
created

MarshalXml::processNodes()   D

Complexity

Conditions 9
Paths 31

Size

Total Lines 43
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 29
CRAP Score 9

Importance

Changes 0
Metric Value
dl 0
loc 43
ccs 29
cts 29
cp 1
rs 4.909
c 0
b 0
f 0
cc 9
eloc 29
nc 31
nop 2
crap 9
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace KingsonDe\Marshal;
6
7
use KingsonDe\Marshal\Data\Collection;
8
use KingsonDe\Marshal\Data\CollectionCallable;
9
use KingsonDe\Marshal\Data\DataStructure;
10
use KingsonDe\Marshal\Exception\XmlSerializeException;
11
12
/**
13
 * @method static string serializeItem(AbstractMapper $mapper, ...$data)
14
 * @method static string serializeItemCallable(callable $mappingFunction, ...$data)
15
 */
16
class MarshalXml extends Marshal {
17
18
    const ATTRIBUTES_KEY = '@attributes';
19
    const DATA_KEY       = '@data';
20
    const CDATA_KEY      = '@cdata';
21
22
    /**
23
     * @var string
24
     */
25
    protected static $version = '1.0';
26
27
    /**
28
     * @var string
29
     */
30
    protected static $encoding = 'UTF-8';
31
32 1
    public static function setVersion(string $version) {
33 1
        static::$version = $version;
34 1
    }
35
36 1
    public static function setEncoding(string $encoding) {
37 1
        static::$encoding = $encoding;
38 1
    }
39
40
    /**
41
     * @param DataStructure $dataStructure
42
     * @return string
43
     * @throws \KingsonDe\Marshal\Exception\XmlSerializeException
44
     */
45 8
    public static function serialize(DataStructure $dataStructure) {
46 8
        if ($dataStructure instanceof Collection || $dataStructure instanceof CollectionCallable) {
47 1
            throw new XmlSerializeException('Collections in XML cannot be generated at root level.');
48
        }
49
50 7
        $data = static::buildDataStructure($dataStructure);
51
52
        try {
53 7
            $xml  = new \DOMDocument(static::$version, static::$encoding);
54
55 7
            if (null === $data) {
56 1
                $xmlRootNode = $xml->createElement('root');
57 1
                $xml->appendChild($xmlRootNode);
58
59 1
                return $xml->saveXML();
60
            }
61
62 6
            \reset($data);
63 6
            $rootNode = \key($data);
64
65 6
            if (isset($data[$rootNode][static::DATA_KEY])) {
66 1
                $xmlRootNode = $xml->createElement($rootNode, static::castValueToString($data[$rootNode][static::DATA_KEY]));
67 1
                unset($data[$rootNode][static::DATA_KEY]);
68 5
            } elseif (isset($data[$rootNode][static::CDATA_KEY])) {
69 1
                $xmlRootNode  = $xml->createElement($rootNode);
70 1
                $cdataSection = $xml->createCDATASection(static::castValueToString($data[$rootNode][static::CDATA_KEY]));
71 1
                $xmlRootNode->appendChild($cdataSection);
72 1
                unset($data[$rootNode][static::CDATA_KEY]);
73
            } else {
74 4
                $xmlRootNode = $xml->createElement($rootNode);
75
            }
76
77 6
            if (isset($data[$rootNode][static::ATTRIBUTES_KEY])) {
78 3
                static::addAttributes($data[$rootNode][static::ATTRIBUTES_KEY], $xmlRootNode);
79 3
                unset($data[$rootNode][static::ATTRIBUTES_KEY]);
80
            }
81 6
            $xml->appendChild($xmlRootNode);
82
83 6
            static::processNodes($data[$rootNode], $xmlRootNode);
84
85 5
            return $xml->saveXML();
86 1
        } catch (\Exception $e) {
87 1
            throw new XmlSerializeException($e->getMessage(), $e->getCode(), $e);
88
        }
89
    }
90
91 1
    public static function serializeCollection(AbstractMapper $mapper, ...$data) {
92 1
        throw new XmlSerializeException('Collections in XML cannot be generated at root level.');
93
    }
94
95 1
    public static function serializeCollectionCallable(callable $mappingFunction, ...$data) {
96 1
        throw new XmlSerializeException('Collections in XML cannot be generated at root level.');
97
    }
98
99 6
    protected static function processNodes(array $nodes, \DOMElement $parentXmlNode) {
100 6
        foreach ($nodes as $node => $value) {
101 4
            $attributes = [];
102 4
            $cdata      = false;
103
104 4
            if (isset($value[static::ATTRIBUTES_KEY])) {
105 2
                $attributes = $value[static::ATTRIBUTES_KEY];
106 2
                unset($value[static::ATTRIBUTES_KEY]);
107
            }
108
109 4
            if (isset($value[static::DATA_KEY])) {
110 2
                $value = $value[static::DATA_KEY];
111 4
            } elseif (isset($value[static::CDATA_KEY])) {
112 2
                $value = $value[static::CDATA_KEY];
113 2
                $cdata = true;
114
            }
115
116
            // new node with scalar value
117 4
            if (\is_scalar($value)) {
118 4
                if ($cdata) {
119 2
                    $xmlNode      = $parentXmlNode->ownerDocument->createElement($node);
120 2
                    $cdataSection = $parentXmlNode->ownerDocument->createCDATASection(static::castValueToString($value));
121 2
                    $xmlNode->appendChild($cdataSection);
122
                } else {
123 3
                    $xmlNode = $parentXmlNode->ownerDocument->createElement($node, static::castValueToString($value));
124
                }
125 3
                static::addAttributes($attributes, $xmlNode);
126 3
                $parentXmlNode->appendChild($xmlNode);
127 3
                continue;
128
            }
129
130
            // node collection of the same type
131 2
            if (\is_int($node)) {
132 2
                static::processNodes($value, $parentXmlNode);
133 2
                continue;
134
            }
135
136
            // new node that might contain other nodes
137 2
            $xmlNode = $parentXmlNode->ownerDocument->createElement($node);
138 2
            static::addAttributes($attributes, $xmlNode);
139 2
            $parentXmlNode->appendChild($xmlNode);
140 2
            if (\is_array($value)) {
141 2
                static::processNodes($value, $xmlNode);
142
            }
143
        }
144 5
    }
145
146 4
    protected static function addAttributes(array $attributes, \DOMElement $xmlNode) {
147 4
        foreach ($attributes as $name => $value) {
148 3
            $xmlNode->setAttribute($name, static::castValueToString($value));
149
        }
150 4
    }
151
152 6
    protected static function castValueToString($value): string {
153 6
        return (\is_string($value) ? $value : var_export($value, true));
154
    }
155
}
156