XmlWriterSerializer::isExtensionEnabled()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 4
cp 0
rs 10
c 0
b 0
f 0
cc 2
eloc 2
nc 2
nop 1
crap 6
1
<?php
2
3
namespace Fxmlrpc\Serialization\Serializer;
4
5
use Fxmlrpc\Serialization\Exception\InvalidTypeException;
6
use Fxmlrpc\Serialization\ExtensionSupport;
7
use Fxmlrpc\Serialization\Serializer;
8
use Fxmlrpc\Serialization\Value\Base64;
9
10
/**
11
 * Serializer creates XML from native PHP types using XML Writer extension.
12
 *
13
 * @author Lars Strojny <[email protected]>
14
 */
15
final class XmlWriterSerializer implements Serializer, ExtensionSupport
16
{
17
    /**
18
     * @var \XMLWriter
19
     */
20
    private $writer;
21
22
    /**
23
     * @var array
24
     */
25
    private $extensions = [];
26
27
    public function __construct()
28
    {
29
        $this->writer = new \XMLWriter();
30
        $this->writer->openMemory();
31
    }
32
33
    /**
34
     * {@inheritdoc}
35
     */
36
    public function enableExtension($extension)
37
    {
38
        $this->extensions[$extension] = true;
39
    }
40
41
    /**
42
     * {@inheritdoc}
43
     */
44
    public function disableExtension($extension)
45
    {
46
        $this->extensions[$extension] = false;
47
    }
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function isExtensionEnabled($extension)
53
    {
54
        return isset($this->extensions[$extension]) ? $this->extensions[$extension] : true;
55
    }
56
57
    /**
58
     * {@inheritdoc}
59
     */
60
    public function serialize($methodName, array $params = [])
61
    {
62
        $writer = $this->writer;
63
64
        $writer->startDocument('1.0', 'UTF-8');
65
        $writer->startElement('methodCall');
66
        $writer->writeElement('methodName', $methodName);
67
        $writer->startElement('params');
68
69
        $endNode = static function () use ($writer) {
70
            $writer->endElement();
71
        };
72
        $valueNode = static function () use ($writer) {
73
            $writer->startElement('value');
74
        };
75
        $paramNode = static function () use ($writer) {
76
            $writer->startElement('param');
77
        };
78
79
        $toBeVisited = [];
80
        foreach (array_reverse($params) as $param) {
81
            $toBeVisited[] = $endNode;
82
            $toBeVisited[] = $param;
83
            $toBeVisited[] = $paramNode;
84
        }
85
86
        $nilTagName = $this->isExtensionEnabled(ExtensionSupport::EXTENSION_NIL) ? 'nil' : 'string';
87
88
        while ($toBeVisited) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $toBeVisited of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
89
            $node = array_pop($toBeVisited);
90
            $type = gettype($node);
91
92
            if ($type === 'string') {
93
                $writer->startElement('value');
94
                $writer->writeElement('string', $node);
95
                $writer->endElement();
96
            } elseif ($type === 'integer') {
97
                $writer->startElement('value');
98
                $writer->writeElement('int', $node);
99
                $writer->endElement();
100
            } elseif ($type === 'double') {
101
                if (!isset($precision)) {
102
                    $precision = ini_get('precision');
103
                }
104
105
                $writer->startElement('value');
106
                $writer->writeElement('double', $node);
107
                $writer->endElement();
108
            } elseif ($type === 'boolean') {
109
                $writer->startElement('value');
110
                $writer->writeElement('boolean', $node ? '1' : '0');
111
                $writer->endElement();
112
            } elseif ($type === 'NULL') {
113
                $writer->startElement('value');
114
                $writer->writeElement($nilTagName);
115
                $writer->endElement();
116
            } elseif ($type === 'array') {
117
                /* Find out if it is a struct or an array */
118
                $smallestIndex = 0;
119
                foreach ($node as $smallestIndex => &$child) {
120
                    break;
121
                }
122
                $isStruct = !is_int($smallestIndex);
123
                if (!$isStruct) {
124
                    $length = count($node) + $smallestIndex;
125
                    for ($index = $smallestIndex; $index < $length; ++$index) {
126
                        if (!isset($node[$index])) {
127
                            $isStruct = true;
128
                            break;
129
                        }
130
                    }
131
                }
132
133
                if (!$isStruct) {
134
                    $toBeVisited[] = $endNode;
135
                    $toBeVisited[] = $endNode;
136
                    $toBeVisited[] = $endNode;
137
                    foreach (array_reverse($node) as $value) {
138
                        $toBeVisited[] = $value;
139
                    }
140
                    $toBeVisited[] = static function () use ($writer) {
141
                        $writer->startElement('array');
142
                        $writer->startElement('data');
143
                    };
144
                    $toBeVisited[] = $valueNode;
145
                } else {
146
                    struct:
147
                    $toBeVisited[] = $endNode;
148
                    $toBeVisited[] = $endNode;
149
                    foreach (array_reverse($node, true) as $key => $value) {
150
                        $toBeVisited[] = $endNode;
151
                        $toBeVisited[] = $value;
152
                        $toBeVisited[] = static function () use ($writer, $key) {
153
                            $writer->writeElement('name', $key);
154
                        };
155
                        $toBeVisited[] = static function () use ($writer) {
156
                            $writer->startElement('member');
157
                        };
158
                    }
159
                    $toBeVisited[] = static function () use ($writer) {
160
                        $writer->startElement('struct');
161
                    };
162
                    $toBeVisited[] = $valueNode;
163
                }
164
            } elseif ($type === 'object') {
165
                if ($node instanceof \Closure) {
166
                    $node();
167
                } elseif ($node instanceof \DateTime) {
168
                    $writer->startElement('value');
169
                    $writer->writeElement('dateTime.iso8601', $node->format('Ymd\TH:i:s'));
170
                    $writer->endElement();
171
                } elseif ($node instanceof Base64) {
172
                    $writer->startElement('value');
173
                    $writer->writeElement('base64', $node->getEncoded()."\n");
174
                    $writer->endElement();
175
                } else {
176
                    $node = get_object_vars($node);
177
                    goto struct;
178
                }
179
            } elseif ($type === 'resource') {
180
                throw new InvalidTypeException($node);
181
            }
182
        }
183
184
        $writer->endElement();
185
        $writer->endElement();
186
187
        $xml = $writer->flush(true);
188
189
        // NativeSerializer does not inject a newline after the declaration
190
        if ($xml[38] === "\n") {
191
            $xml = substr($xml, 0, 38).substr($xml, 39);
192
        }
193
194
        return $xml;
195
    }
196
}
197