Completed
Push — master ( 0e6243...c708c9 )
by Bas
04:31 queued 02:09
created

SoapDeserializationVisitor::elementToArray()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 13
ccs 8
cts 8
cp 1
crap 3
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
namespace DMT\Soap\Serializer;
4
5
use JMS\Serializer\Exception\InvalidArgumentException;
6
use JMS\Serializer\XmlDeserializationVisitor;
7
8
/**
9
 * Class SoapDeserializationVisitor
10
 *
11
 * @package DMT\Soap
12
 */
13
class SoapDeserializationVisitor extends XmlDeserializationVisitor implements SoapNamespaceInterface
14
{
15
    /**
16
     * @param string $data
17
     *
18
     * @return mixed|\SimpleXMLElement
19
     * @throws InvalidArgumentException
20
     * @throws SoapFaultException
21
     */
22 10
    public function prepare($data)
23
    {
24
        /** @var \SimpleXMLElement $element */
25 10
        $element = parent::prepare($data);
26
27 10
        $version = array_search(current($element->getNamespaces()), static::SOAP_NAMESPACES);
28 10
        if (!$version) {
29 1
            throw new InvalidArgumentException('Unsupported SOAP version');
30
        }
31
32 9
        $element = $this->moveChildNamespacesToEnvelope($element);
33
34 9
        $messages = $element->xpath('*[local-name()="Body"]/*');
35 9
        if (count($messages) === 1) {
36 9
            $element = $messages[0];
37
        }
38
39 9
        if ($element->getName() === 'Fault') {
40 5
            if ($version == static::SOAP_1_1) {
41 2
                $this->throwSoap11Fault($element);
42
            } else {
43 3
                $this->throwSoap12Fault($element);
44
            }
45
        }
46
47 4
        return $element;
48
    }
49
50
    /**
51
     * Move all underlying namespaces to root element.
52
     *
53
     * @param \SimpleXMLElement $element
54
     *
55
     * @return \SimpleXMLElement
56
     */
57 9
    protected function moveChildNamespacesToEnvelope(\SimpleXMLElement $element): \SimpleXMLElement
58
    {
59 9
        $dom = dom_import_simplexml($element);
60
61 9
        foreach ($element->getNamespaces(true) as $prefix => $namespace) {
62 9
            if (!in_array($namespace, $element->getDocNamespaces())) {
63 9
                $dom->setAttributeNS('http://www.w3.org/2000/xmlns/', $prefix ? 'xmlns:' . $prefix : 'xmlns', $namespace);
64
            }
65
        }
66
67 9
        return parent::prepare($dom->ownerDocument->saveXML());
68
    }
69
70
    /**
71
     * @param \SimpleXMLElement $fault
72
     *
73
     * @throws SoapFaultException
74
     */
75 2
    protected function throwSoap11Fault(\SimpleXMLElement $fault)
76
    {
77 2
        $faultcode = $faultstring = '';
78 2
        $faultactor = $detail = null;
79
80 2
        extract($this->elementToArray($fault), EXTR_IF_EXISTS);
81
82 2
        throw new SoapFaultException($this->removePrefix($faultcode), $faultstring, $faultactor, $detail);
83
    }
84
85
    /**
86
     * @param \SimpleXMLElement $fault
87
     *
88
     * @throws SoapFaultException
89
     */
90 3
    protected function throwSoap12Fault(\SimpleXMLElement $fault)
91
    {
92 3
        $lang = substr(locale_get_default(), 0, 2);
93 3
        $path = '*[local-name()="Reason"]/*[local-name()="Text" and @xml:lang="' . $lang . '"]';
94
95 3
        $messages = $fault->xpath($path);
96 3
        if (count($messages) === 0) {
97 1
            $messages = $fault->xpath('*[local-name()="Reason"]/*[local-name()="Text"]');
98
        }
99
100 3
        $code = array_map(
101 3
            [$this, 'removePrefix'],
102 3
            $fault->xpath('//*[local-name()="Code" or local-name()="Subcode"]/*[local-name()="Value"]')
103
        );
104
105 3
        $node = $fault->xpath('*[local-name()="Node"]')[0] ?? null;
106 3
        $detail = $fault->xpath('*[local-name()="Detail"]')[0] ?? null;
107
108 3
        if ($detail !== null) {
109 3
            $detail = $this->elementToArray($detail);
110
        }
111
112 3
        throw new SoapFaultException(implode('.', $code), $messages[0], $node, $detail);
113
    }
114
115
    /**
116
     * @param \SimpleXMLElement $element
117
     *
118
     * @return array
119
     */
120 5
    protected function elementToArray(\SimpleXMLElement $element): array
121
    {
122 5
        $result = [];
123 5
        foreach ($element->xpath('*') as $node) {
124 5
            if (count($node->xpath('*')) > 0) {
125 2
                $value = $this->elementToArray($node);
126
            } else {
127 5
                $value = strval($node);
128
            }
129 5
            $result = array_merge_recursive($result, [$node->getName() => $value]);
130
        }
131
132 5
        return $result;
133
    }
134
135
    /**
136
     * Remove a prefix from text node.
137
     *
138
     * @param string $value A node value containing a namespace prefix, eg SOAP-ENV:Client.
139
     *
140
     * @return string
141
     */
142 5
    protected function removePrefix(string $value): string
143
    {
144 5
        return preg_replace('~^(.+:)?(.*)$~', "$2", $value);
145
    }
146
}
147