Completed
Push — 3.0 ( 705f3e...f903fb )
by Alexander
12:59
created

XmlResponseFormatter::format()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 19
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 5

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 14
cts 14
cp 1
rs 8.8571
c 0
b 0
f 0
cc 5
eloc 14
nc 12
nop 1
crap 5
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\web;
9
10
use DOMDocument;
11
use DOMElement;
12
use DOMException;
13
use DOMText;
14
use yii\base\Arrayable;
15
use yii\base\Component;
16
use yii\helpers\StringHelper;
17
18
/**
19
 * XmlResponseFormatter formats the given data into an XML response content.
20
 *
21
 * It is used by [[Response]] to format response data.
22
 *
23
 * @author Qiang Xue <[email protected]>
24
 * @since 2.0
25
 */
26
class XmlResponseFormatter extends Component implements ResponseFormatterInterface
27
{
28
    /**
29
     * @var string the Content-Type header for the response
30
     */
31
    public $contentType = 'application/xml';
32
    /**
33
     * @var string the XML version
34
     */
35
    public $version = '1.0';
36
    /**
37
     * @var string the XML encoding. If not set, it will use the value of [[Response::charset]].
38
     */
39
    public $encoding;
40
    /**
41
     * @var string the name of the root element. If set to false, null or is empty then no root tag should be added.
42
     */
43
    public $rootTag = 'response';
44
    /**
45
     * @var string the name of the elements that represent the array elements with numeric keys.
46
     */
47
    public $itemTag = 'item';
48
    /**
49
     * @var bool whether to interpret objects implementing the [[\Traversable]] interface as arrays.
50
     * Defaults to `true`.
51
     * @since 2.0.7
52
     */
53
    public $useTraversableAsArray = true;
54
    /**
55
     * @var bool if object tags should be added
56
     * @since 2.0.11
57
     */
58
    public $useObjectTags = true;
59
60
61
    /**
62
     * Formats the specified response.
63
     * @param Response $response the response to be formatted.
64
     */
65 20
    public function format($response)
66
    {
67 20
        $charset = $this->encoding === null ? $response->charset : $this->encoding;
68 20
        if (stripos($this->contentType, 'charset') === false) {
69 20
            $this->contentType .= '; charset=' . $charset;
70
        }
71 20
        $response->setHeader('Content-Type', $this->contentType);
72 20
        if ($response->data !== null) {
73 19
            $dom = new DOMDocument($this->version, $charset);
74 19
            if (!empty($this->rootTag)) {
75 18
                $root = new DOMElement($this->rootTag);
76 18
                $dom->appendChild($root);
77 18
                $this->buildXml($root, $response->data);
78
            } else {
79 1
                $this->buildXml($dom, $response->data);
0 ignored issues
show
Documentation introduced by
$dom is of type object<DOMDocument>, but the function expects a object<DOMElement>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
80
            }
81 19
            $response->content = $dom->saveXML();
82
        }
83 20
    }
84
85
    /**
86
     * @param DOMElement $element
87
     * @param mixed $data
88
     */
89 19
    protected function buildXml($element, $data)
90
    {
91 19
        if (is_array($data) ||
92 19
            ($data instanceof \Traversable && $this->useTraversableAsArray && !$data instanceof Arrayable)
93
        ) {
94 12
            foreach ($data as $name => $value) {
95 11
                if (is_int($name) && is_object($value)) {
96 3
                    $this->buildXml($element, $value);
97 11
                } elseif ((is_array($value) && !isset($value['xml-attributes'])) || is_object($value)) {
98 3
                    $child = new DOMElement($this->getValidXmlElementName($name));
99 3
                    $element->appendChild($child);
100 3
                    $this->buildXml($child, $value);
101
                } else {
102 11
                    $child = new DOMElement($this->getValidXmlElementName($name));
103 11
                    $element->appendChild($child);
104 11
                    if(is_array($value) && isset($value['xml-attributes'])){
105 2
                        foreach($value['xml-attributes'] as $attribute => $val){
106 2
                            $child->setAttribute($attribute, $val);
107
                        }
108 2
                        $child->appendChild(new DOMText($this->formatScalarValue($value['value'])));
109
                    }else{
110 12
                        $child->appendChild(new DOMText($this->formatScalarValue($value)));
111
                    }
112
                }
113
            }
114 13
        } elseif (is_object($data)) {
115 6
            if ($this->useObjectTags) {
116 5
                $child = new DOMElement(StringHelper::basename(get_class($data)));
117 5
                $element->appendChild($child);
118
            } else {
119 1
                $child = $element;
120
            }
121 6
            if ($data instanceof Arrayable) {
122 1
                $this->buildXml($child, $data->toArray());
123
            } else {
124 5
                $array = [];
125 5
                foreach ($data as $name => $value) {
126 5
                    $array[$name] = $value;
127
                }
128 6
                $this->buildXml($child, $array);
129
            }
130
        } else {
131 7
            $element->appendChild(new DOMText($this->formatScalarValue($data)));
132
        }
133 19
    }
134
135
    /**
136
     * Formats scalar value to use in XML text node.
137
     *
138
     * @param int|string|bool|float $value a scalar value.
139
     * @return string string representation of the value.
140
     * @since 2.0.11
141
     */
142 18
    protected function formatScalarValue($value)
143
    {
144 18
        if ($value === true) {
145 2
            return 'true';
146
        }
147 17
        if ($value === false) {
148 2
            return 'false';
149
        }
150 16
        if (is_float($value)) {
151
            return StringHelper::floatToString($value);
152
        }
153 16
        return (string) $value;
154
    }
155
156
    /**
157
     * Returns element name ready to be used in DOMElement if
158
     * name is not empty, is not int and is valid.
159
     *
160
     * Falls back to [[itemTag]] otherwise.
161
     *
162
     * @param mixed $name
163
     * @return string
164
     * @since 2.0.12
165
     */
166 11
    protected function getValidXmlElementName($name)
167
    {
168 11
        if (empty($name) || is_int($name) || !$this->isValidXmlName($name)) {
169 4
            return $this->itemTag;
170
        }
171
172 9
        return $name;
173
    }
174
175
    /**
176
     * Checks if name is valid to be used in XML.
177
     *
178
     * @param mixed $name
179
     * @return bool
180
     * @see http://stackoverflow.com/questions/2519845/how-to-check-if-string-is-a-valid-xml-element-name/2519943#2519943
181
     * @since 2.0.12
182
     */
183 9
    protected function isValidXmlName($name)
184
    {
185
        try {
186 9
            new DOMElement($name);
187 9
            return true;
188 1
        } catch (DOMException $e) {
189 1
            return false;
190
        }
191
    }
192
}
193